Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support requesting Jakarta Provider types. #4510

Merged
merged 1 commit into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions java/dagger/internal/codegen/base/FrameworkTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package dagger.internal.codegen.base;

import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;

import androidx.room.compiler.processing.XType;
Expand All @@ -31,11 +32,7 @@
public final class FrameworkTypes {
// TODO(erichang): Add the Jakarta Provider here
private static final ImmutableSet<ClassName> PROVISION_TYPES =
ImmutableSet.of(
TypeNames.PROVIDER,
TypeNames.JAKARTA_PROVIDER,
TypeNames.LAZY,
TypeNames.MEMBERS_INJECTOR);
ImmutableSet.of(TypeNames.PROVIDER, TypeNames.LAZY, TypeNames.MEMBERS_INJECTOR);

// NOTE(beder): ListenableFuture is not considered a producer framework type because it is not
// defined by the framework, so we can't treat it specially in ordinary Dagger.
Expand All @@ -45,15 +42,13 @@ public final class FrameworkTypes {
private static final ImmutableSet<ClassName> ALL_FRAMEWORK_TYPES =
ImmutableSet.<ClassName>builder().addAll(PROVISION_TYPES).addAll(PRODUCTION_TYPES).build();

public static final ImmutableSet<ClassName> SET_VALUE_FRAMEWORK_TYPES =
private static final ImmutableSet<ClassName> SET_VALUE_FRAMEWORK_TYPES =
ImmutableSet.of(TypeNames.PRODUCED);

public static final ImmutableSet<ClassName> MAP_VALUE_FRAMEWORK_TYPES =
ImmutableSet.of(
TypeNames.PRODUCED,
TypeNames.PRODUCER,
TypeNames.PROVIDER,
TypeNames.JAKARTA_PROVIDER);
MapType.VALID_FRAMEWORK_REQUEST_KINDS.stream()
.map(RequestKinds::frameworkClassName)
.collect(toImmutableSet());

/** Returns true if the type represents a producer-related framework type. */
public static boolean isProducerType(XType type) {
Expand Down
21 changes: 6 additions & 15 deletions java/dagger/internal/codegen/base/MapType.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@
@AutoValue
public abstract class MapType {
// TODO(b/28555349): support PROVIDER_OF_LAZY here too
// TODO(b/376124787): We could consolidate this with a similar list in FrameworkTypes
// if we had a better way to go from RequestKind to framework ClassName or vice versa
/** The valid framework request kinds allowed on a multibinding map value. */
private static final ImmutableSet<RequestKind> VALID_FRAMEWORK_REQUEST_KINDS =
public static final ImmutableSet<RequestKind> VALID_FRAMEWORK_REQUEST_KINDS =
ImmutableSet.of(RequestKind.PROVIDER, RequestKind.PRODUCER, RequestKind.PRODUCED);

private XType type;
Expand Down Expand Up @@ -109,19 +107,12 @@ public XType unwrappedFrameworkValueType() {
*/
public RequestKind valueRequestKind() {
checkArgument(!isRawType());
RequestKind requestKind = RequestKinds.getRequestKind(valueType());
if (VALID_FRAMEWORK_REQUEST_KINDS.contains(requestKind)) {
return requestKind;
} else if (requestKind == RequestKind.PROVIDER_OF_LAZY) {
// This is kind of a weird case. We don't support Map<K, Lazy<V>>, so we also don't support
// Map<K, Provider<Lazy<V>>> directly. However, if the user bound that themselves, we don't
// want that to get confused as a normal instance request, so return PROVIDER here.
return RequestKind.PROVIDER;
} else {
// Not all RequestKinds are supported, so if there's a map value that matches an unsupported
// RequestKind, just treat it like it is a normal instance request.
return RequestKind.INSTANCE;
for (RequestKind frameworkRequestKind : VALID_FRAMEWORK_REQUEST_KINDS) {
if (valuesAreTypeOf(RequestKinds.frameworkClassName(frameworkRequestKind))) {
return frameworkRequestKind;
}
}
return RequestKind.INSTANCE;
}

/** {@code true} if {@code type} is a {@link java.util.Map} type. */
Expand Down
9 changes: 1 addition & 8 deletions java/dagger/internal/codegen/base/RequestKinds.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ public static TypeName requestTypeName(RequestKind requestKind, TypeName keyType

private static final ImmutableMap<RequestKind, ClassName> FRAMEWORK_CLASSES =
ImmutableMap.of(
// Default to the javax Provider since that is what is used for the binding graph
// representation.
PROVIDER, TypeNames.PROVIDER,
LAZY, TypeNames.LAZY,
PRODUCER, TypeNames.PRODUCER,
Expand All @@ -113,15 +111,10 @@ public static RequestKind getRequestKind(XType type) {
return RequestKind.INSTANCE;
}

if ((isTypeOf(type, TypeNames.PROVIDER) || isTypeOf(type, TypeNames.JAKARTA_PROVIDER))
&& isTypeOf(unwrapType(type), TypeNames.LAZY)) {
if (isTypeOf(type, TypeNames.PROVIDER) && isTypeOf(unwrapType(type), TypeNames.LAZY)) {
return RequestKind.PROVIDER_OF_LAZY;
}

if (isTypeOf(type, TypeNames.JAKARTA_PROVIDER)) {
return RequestKind.PROVIDER;
}

return FRAMEWORK_CLASSES.keySet().stream()
.filter(kind -> isTypeOf(type, FRAMEWORK_CLASSES.get(kind)))
.collect(toOptional())
Expand Down
13 changes: 8 additions & 5 deletions java/dagger/internal/codegen/binding/ComponentDeclarations.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimaps;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.WildcardTypeName;
import dagger.internal.codegen.base.DaggerSuperficialValidation;
import dagger.internal.codegen.base.FrameworkTypes;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.DaggerAnnotation;
import dagger.internal.codegen.model.Key;
Expand All @@ -40,6 +38,11 @@

/** Stores the bindings and declarations of a component by key. */
final class ComponentDeclarations {
private static final ImmutableSet<TypeName> MAP_FRAMEWORK_TYPENAMES =
ImmutableSet.of(TypeNames.PROVIDER, TypeNames.PRODUCER, TypeNames.PRODUCED);
private static final ImmutableSet<TypeName> SET_FRAMEWORK_TYPENAMES =
ImmutableSet.of(TypeNames.PRODUCED);

private final KeyFactory keyFactory;
private final ImmutableSetMultimap<Key, ContributionBinding> bindings;
private final ImmutableSetMultimap<Key, DelegateDeclaration> delegates;
Expand Down Expand Up @@ -317,14 +320,14 @@ private static TypeName unwrapMultibindingTypeName(TypeName typeName) {
return ParameterizedTypeName.get(
mapTypeName.rawType,
mapKeyTypeName,
unwrapFrameworkTypeName(mapValueTypeName, FrameworkTypes.MAP_VALUE_FRAMEWORK_TYPES));
unwrapFrameworkTypeName(mapValueTypeName, MAP_FRAMEWORK_TYPENAMES));
}
if (isValidSetMultibindingTypeName(typeName)) {
ParameterizedTypeName setTypeName = (ParameterizedTypeName) typeName;
TypeName setValueTypeName = getOnlyElement(setTypeName.typeArguments);
return ParameterizedTypeName.get(
setTypeName.rawType,
unwrapFrameworkTypeName(setValueTypeName, FrameworkTypes.SET_VALUE_FRAMEWORK_TYPES));
unwrapFrameworkTypeName(setValueTypeName, SET_FRAMEWORK_TYPENAMES));
}
return typeName;
}
Expand All @@ -351,7 +354,7 @@ private static boolean isValidSetMultibindingTypeName(TypeName typeName) {
}

private static TypeName unwrapFrameworkTypeName(
TypeName typeName, ImmutableSet<ClassName> frameworkTypeNames) {
TypeName typeName, ImmutableSet<TypeName> frameworkTypeNames) {
if (typeName instanceof ParameterizedTypeName) {
ParameterizedTypeName parameterizedTypeName = (ParameterizedTypeName) typeName;
if (frameworkTypeNames.contains(parameterizedTypeName.rawType)) {
Expand Down
11 changes: 8 additions & 3 deletions java/dagger/internal/codegen/binding/KeyFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import dagger.Binds;
import dagger.BindsOptionalOf;
import dagger.internal.codegen.base.ContributionType;
import dagger.internal.codegen.base.FrameworkTypes;
import dagger.internal.codegen.base.MapType;
import dagger.internal.codegen.base.OptionalType;
import dagger.internal.codegen.base.RequestKinds;
Expand Down Expand Up @@ -98,7 +97,10 @@ private XType optionalOf(XType type) {

/** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
private XType mapOfFrameworkType(XType keyType, ClassName frameworkClassName, XType valueType) {
checkArgument(FrameworkTypes.MAP_VALUE_FRAMEWORK_TYPES.contains(frameworkClassName));
checkArgument(
MapType.VALID_FRAMEWORK_REQUEST_KINDS.stream()
.map(RequestKinds::frameworkClassName)
.anyMatch(frameworkClassName::equals));
return mapOf(
keyType,
processingEnv.getDeclaredType(
Expand Down Expand Up @@ -315,7 +317,10 @@ public Key unwrapMapValueType(Key key) {
* type.
*/
private Key wrapMapValue(Key key, ClassName frameworkClassName) {
checkArgument(FrameworkTypes.MAP_VALUE_FRAMEWORK_TYPES.contains(frameworkClassName));
checkArgument(
MapType.VALID_FRAMEWORK_REQUEST_KINDS.stream()
.map(RequestKinds::frameworkClassName)
.anyMatch(frameworkClassName::equals));
if (MapType.isMap(key)) {
MapType mapType = MapType.from(key);
if (!mapType.isRawType() && !mapType.valuesAreTypeOf(frameworkClassName)) {
Expand Down
8 changes: 8 additions & 0 deletions java/dagger/internal/codegen/binding/SourceFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ public final class SourceFiles {
dependency -> {
ClassName frameworkClassName =
frameworkTypeMapper.getFrameworkType(dependency.kind()).frameworkClassName();
// Remap factory fields back to javax.inject.Provider to maintain backwards compatibility
// for now. In a future release, we should change this to Dagger Provider. This will still
// be a breaking change, but keeping compatibility for a while should reduce the
// likelihood of breakages as it would require components built at much older versions
// using factories built at newer versions to break.
if (frameworkClassName.equals(TypeNames.DAGGER_PROVIDER)) {
frameworkClassName = TypeNames.PROVIDER;
}
return FrameworkField.create(
ParameterizedTypeName.get(
frameworkClassName,
Expand Down
1 change: 0 additions & 1 deletion java/dagger/internal/codegen/javapoet/TypeNames.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ public final class TypeNames {
public static final ClassName MEMBERS_INJECTORS =
ClassName.get("dagger.internal", "MembersInjectors");
public static final ClassName PROVIDER = ClassName.get("javax.inject", "Provider");
public static final ClassName JAKARTA_PROVIDER = ClassName.get("jakarta.inject", "Provider");
public static final ClassName DAGGER_PROVIDER = ClassName.get("dagger.internal", "Provider");
public static final ClassName DAGGER_PROVIDERS = ClassName.get("dagger.internal", "Providers");
public static final ClassName PROVIDER_OF_LAZY =
Expand Down
84 changes: 3 additions & 81 deletions java/dagger/internal/codegen/writing/FactoryGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
import static dagger.internal.codegen.model.BindingKind.INJECTION;
Expand All @@ -49,14 +48,12 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.codegen.base.SourceFileGenerator;
Expand All @@ -76,7 +73,6 @@
import dagger.internal.codegen.model.Scope;
import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import javax.inject.Inject;
Expand Down Expand Up @@ -139,7 +135,7 @@ private TypeSpec.Builder factoryBuilder(ContributionBinding binding) {

return factoryBuilder
.addMethod(getMethod(binding, factoryFields))
.addMethods(staticCreateMethod(binding, factoryFields))
.addMethod(staticCreateMethod(binding, factoryFields))
.addMethod(staticProvisionMethod(binding));
}

Expand Down Expand Up @@ -197,12 +193,10 @@ private MethodSpec constructorMethod(FactoryFields factoryFields) {
// Provider<Baz> bazProvider) {
// return new FooModule_ProvidesFooFactory(module, barProvider, bazProvider);
// }
private ImmutableList<MethodSpec> staticCreateMethod(
ContributionBinding binding, FactoryFields factoryFields) {
private MethodSpec staticCreateMethod(ContributionBinding binding, FactoryFields factoryFields) {
// We use a static create method so that generated components can avoid having to refer to the
// generic types of the factory. (Otherwise they may have visibility problems referring to the
// types.)
ImmutableList.Builder<MethodSpec> methodsBuilder = ImmutableList.builder();
MethodSpec.Builder createMethodBuilder =
methodBuilder("create")
.addModifiers(PUBLIC, STATIC)
Expand All @@ -225,32 +219,8 @@ private ImmutableList<MethodSpec> staticCreateMethod(
"return new $T($L)",
parameterizedGeneratedTypeNameForBinding(binding),
parameterNames(parameters));
// If any of the parameters take a Dagger Provider type, we also need to make a
// Javax Provider type for backwards compatibility with components generated at
// an older version.
// Eventually, we will need to remove this and break backwards compatibility
// in order to fully cut the Javax dependency.
if (hasDaggerProviderParams(parameters)) {
methodsBuilder.add(javaxCreateMethod(binding, parameters));
}
}
methodsBuilder.add(createMethodBuilder.build());
return methodsBuilder.build();
}

private MethodSpec javaxCreateMethod(
ContributionBinding binding, ImmutableList<ParameterSpec> parameters) {
ImmutableList<ParameterSpec> remappedParams = remapParamsToJavaxProvider(parameters);
return methodBuilder("create")
.addModifiers(PUBLIC, STATIC)
.returns(parameterizedGeneratedTypeNameForBinding(binding))
.addTypeVariables(bindingTypeElementTypeVariableNames(binding))
.addParameters(remappedParams)
.addStatement(
"return new $T($L)",
parameterizedGeneratedTypeNameForBinding(binding),
wrappedParametersCodeBlock(remappedParams))
.build();
return createMethodBuilder.build();
}

// Example 1: Provision binding.
Expand Down Expand Up @@ -409,54 +379,6 @@ private static Optional<TypeName> factoryTypeName(ContributionBinding binding) {
: Optional.of(factoryOf(providedTypeName(binding)));
}

// Open for sharing with ProducerFactoryGenerator and MembersInjectorGenerator
static boolean hasDaggerProviderParams(List<ParameterSpec> params) {
return params.stream().anyMatch(param -> isDaggerProviderType(param.type));
}

// Open for sharing with ProducerFactoryGenerator and MembersInjectorGenerator
// Returns a code block that represents a parameter list where any javax Provider
// types are wrapped in an asDaggerProvider call
static CodeBlock wrappedParametersCodeBlock(List<ParameterSpec> params) {
return makeParametersCodeBlock(
Lists.transform(
params,
input ->
isProviderType(input.type)
? CodeBlock.of(
"$T.asDaggerProvider($N)", TypeNames.DAGGER_PROVIDERS, input)
: CodeBlock.of("$N", input)));
}

// Open for sharing with ProducerFactoryGenerator and MembersInjectorGenerator
static ImmutableList<ParameterSpec> remapParamsToJavaxProvider(List<ParameterSpec> params) {
return params.stream()
.map(param -> ParameterSpec.builder(
remapDaggerProviderToProvider(param.type), param.name).build())
.collect(toImmutableList());
}

private static boolean isDaggerProviderType(TypeName type) {
return type instanceof ParameterizedTypeName
&& ((ParameterizedTypeName) type).rawType.equals(TypeNames.DAGGER_PROVIDER);
}

private static boolean isProviderType(TypeName type) {
return type instanceof ParameterizedTypeName
&& ((ParameterizedTypeName) type).rawType.equals(TypeNames.PROVIDER);
}

private static TypeName remapDaggerProviderToProvider(TypeName type) {
if (type instanceof ParameterizedTypeName) {
ParameterizedTypeName parameterizedTypeName = (ParameterizedTypeName) type;
if (parameterizedTypeName.rawType.equals(TypeNames.DAGGER_PROVIDER)) {
return ParameterizedTypeName.get(
TypeNames.PROVIDER, parameterizedTypeName.typeArguments.toArray(new TypeName[0]));
}
}
return type;
}

/** Represents the available fields in the generated factory class. */
private static final class FactoryFields {
static FactoryFields create(ContributionBinding binding) {
Expand Down
Loading
Loading