diff --git a/java/dagger/internal/codegen/writing/ComponentImplementation.java b/java/dagger/internal/codegen/writing/ComponentImplementation.java index 5c2678f6705..58c8c93f4ea 100644 --- a/java/dagger/internal/codegen/writing/ComponentImplementation.java +++ b/java/dagger/internal/codegen/writing/ComponentImplementation.java @@ -462,7 +462,6 @@ public final class ShardImplementation implements GeneratedImplementation { private final UniqueNameSet assistedParamNames = new UniqueNameSet(); private final List initializations = new ArrayList<>(); private final SwitchingProviders switchingProviders; - private final LazyClassKeyProviders lazyClassKeyProviders; private final Map cancellations = new LinkedHashMap<>(); private final Map uniqueAssistedName = new LinkedHashMap<>(); private final List componentRequirementInitializations = new ArrayList<>(); @@ -479,7 +478,6 @@ public final class ShardImplementation implements GeneratedImplementation { private ShardImplementation(ClassName name) { this.name = name; this.switchingProviders = new SwitchingProviders(this, processingEnv); - this.lazyClassKeyProviders = new LazyClassKeyProviders(this); if (graph.componentDescriptor().isProduction()) { claimMethodName(CANCELLATION_LISTENER_METHOD_NAME); } @@ -513,10 +511,6 @@ public SwitchingProviders getSwitchingProviders() { return switchingProviders; } - public LazyClassKeyProviders getLazyClassKeyProviders() { - return lazyClassKeyProviders; - } - /** Returns the {@link ComponentImplementation} that owns this shard. */ public ComponentImplementation getComponentImplementation() { return ComponentImplementation.this; @@ -618,7 +612,8 @@ public void addType(TypeSpecKind typeKind, TypeSpec typeSpec) { } /** Adds a {@link Supplier} for the SwitchingProvider for the component. */ - void addTypeSupplier(Supplier typeSpecSupplier) { + @Override + public void addTypeSupplier(Supplier typeSpecSupplier) { typeSuppliers.add(typeSpecSupplier); } diff --git a/java/dagger/internal/codegen/writing/ComponentWrapperImplementation.java b/java/dagger/internal/codegen/writing/ComponentWrapperImplementation.java index 96a042cd72e..a5be3a4f4d0 100644 --- a/java/dagger/internal/codegen/writing/ComponentWrapperImplementation.java +++ b/java/dagger/internal/codegen/writing/ComponentWrapperImplementation.java @@ -23,6 +23,7 @@ import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; +import com.google.common.base.Supplier; import com.google.common.collect.ListMultimap; import com.google.common.collect.MultimapBuilder; import com.squareup.javapoet.ClassName; @@ -34,6 +35,8 @@ import dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind; import dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind; import dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind; +import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; /** Represents the implementation of the generated holder for the components. */ @@ -48,6 +51,7 @@ public final class ComponentWrapperImplementation implements GeneratedImplementa MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build(); private final ListMultimap typeSpecsMap = MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build(); + private final List> typeSuppliers = new ArrayList<>(); @Inject ComponentWrapperImplementation(@TopLevel BindingGraph graph) { @@ -80,6 +84,11 @@ public void addType(TypeSpecKind typeKind, TypeSpec typeSpec) { typeSpecsMap.put(typeKind, typeSpec); } + @Override + public void addTypeSupplier(Supplier typeSpecSupplier) { + typeSuppliers.add(typeSpecSupplier); + } + @Override public TypeSpec generate() { TypeSpec.Builder builder = @@ -92,6 +101,7 @@ public TypeSpec generate() { fieldSpecsMap.asMap().values().forEach(builder::addFields); methodSpecsMap.asMap().values().forEach(builder::addMethods); typeSpecsMap.asMap().values().forEach(builder::addTypes); + typeSuppliers.stream().map(Supplier::get).forEach(builder::addType); return builder.addMethod(constructorBuilder().addModifiers(PRIVATE).build()).build(); } diff --git a/java/dagger/internal/codegen/writing/GeneratedImplementation.java b/java/dagger/internal/codegen/writing/GeneratedImplementation.java index 032a3c41b6e..cea01f7f89e 100644 --- a/java/dagger/internal/codegen/writing/GeneratedImplementation.java +++ b/java/dagger/internal/codegen/writing/GeneratedImplementation.java @@ -16,6 +16,7 @@ package dagger.internal.codegen.writing; +import com.google.common.base.Supplier; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; @@ -41,6 +42,9 @@ public interface GeneratedImplementation { /** Adds the given type to the generated implementation. */ void addType(TypeSpecKind typeKind, TypeSpec typeSpec); + /** Adds a {@link Supplier} for a {@link TypeSpec} to the generated implementation. */ + void addTypeSupplier(Supplier typeSupplier); + /** Returns the {@link TypeSpec} for this generated implementation. */ public TypeSpec generate(); } diff --git a/java/dagger/internal/codegen/writing/LazyClassKeyProviders.java b/java/dagger/internal/codegen/writing/LazyClassKeyProviders.java index 0d97e5b4482..b1a216b8e1b 100644 --- a/java/dagger/internal/codegen/writing/LazyClassKeyProviders.java +++ b/java/dagger/internal/codegen/writing/LazyClassKeyProviders.java @@ -30,88 +30,129 @@ import dagger.internal.codegen.base.UniqueNameSet; import dagger.internal.codegen.javapoet.TypeNames; import dagger.internal.codegen.model.Key; -import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation; import java.util.LinkedHashMap; import java.util.Map; +import javax.inject.Inject; -/** Keeps track of all providers for DaggerMap keys. */ +/** + * Keeps track of all providers for DaggerMap keys. + * + *

Generated class looks like below: + * + *

{@code
+ *  @IdentifierNameString
+ *  static final class  LazyClassKeyProvider {
+ *    static final String com_google_foo_Bar = "com.google.foo.Bar";
+ *    @KeepFieldType static final com.google.foo.Bar com_google_foo_Bar2;
+ * }
+ * }
+ */ public final class LazyClassKeyProviders { - public static final String MAP_KEY_PROVIDER_NAME = "LazyClassKeyProvider"; - private final ClassName mapKeyProviderType; - private final Map entries = new LinkedHashMap<>(); - private final Map keepClassNamesFields = new LinkedHashMap<>(); - private final UniqueNameSet uniqueFieldNames = new UniqueNameSet(); - private final ShardImplementation shardImplementation; - private boolean providerAdded = false; + @PerGeneratedFile + static final class LazyClassKeyProviderCache { + // Map key to its corresponding the field reference expression from LazyClassKeyProvider. + final Map mapKeyToProvider = new LinkedHashMap<>(); + private final Map entries = new LinkedHashMap<>(); + private final Map keepClassNamesFields = new LinkedHashMap<>(); + private final UniqueNameSet uniqueFieldNames = new UniqueNameSet(); + private ClassName mapKeyProviderType; - LazyClassKeyProviders(ShardImplementation shardImplementation) { - String name = shardImplementation.getUniqueClassName(MAP_KEY_PROVIDER_NAME); - mapKeyProviderType = shardImplementation.name().nestedClass(name); - this.shardImplementation = shardImplementation; - } + @Inject + LazyClassKeyProviderCache() {} - /** Returns a reference to a field in LazyClassKeyProvider that corresponds to this binding. */ - CodeBlock getMapKeyExpression(Key key) { - // This is for avoid generating empty LazyClassKeyProvider in codegen tests - if (!providerAdded) { - shardImplementation.addTypeSupplier(this::build); - providerAdded = true; + private CodeBlock getMapKeyExpression(Key key) { + Preconditions.checkArgument( + key.multibindingContributionIdentifier().isPresent() + && key.multibindingContributionIdentifier() + .get() + .bindingMethod() + .xprocessing() + .hasAnnotation(TypeNames.LAZY_CLASS_KEY)); + XAnnotation lazyClassKeyAnnotation = getLazyClassKeyAnnotation(key); + ClassName lazyClassKey = getLazyClassKey(lazyClassKeyAnnotation); + if (mapKeyToProvider.containsKey(lazyClassKey)) { + return mapKeyToProvider.get(lazyClassKey); + } + addField(lazyClassKey, lazyClassKeyAnnotation, mapKeyProviderType.packageName()); + mapKeyToProvider.put( + lazyClassKey, CodeBlock.of("$T.$N", mapKeyProviderType, entries.get(lazyClassKey))); + return mapKeyToProvider.get(lazyClassKey); } - if (!entries.containsKey(key)) { - addField(key); + + private ClassName getLazyClassKey(XAnnotation lazyClassKeyAnnotation) { + return lazyClassKeyAnnotation.getAsType("value").getTypeElement().getClassName(); + } + + private XAnnotation getLazyClassKeyAnnotation(Key key) { + return key.multibindingContributionIdentifier() + .get() + .bindingMethod() + .xprocessing() + .getAnnotation(TypeNames.LAZY_CLASS_KEY); } - return CodeBlock.of("$T.$N", mapKeyProviderType, entries.get(key)); - } - private void addField(Key key) { - Preconditions.checkArgument( - key.multibindingContributionIdentifier().isPresent() - && key.multibindingContributionIdentifier() - .get() - .bindingMethod() - .xprocessing() - .hasAnnotation(TypeNames.LAZY_CLASS_KEY)); - XAnnotation lazyClassKeyAnnotation = - key.multibindingContributionIdentifier() - .get() - .bindingMethod() - .xprocessing() - .getAnnotation(TypeNames.LAZY_CLASS_KEY); - ClassName lazyClassKey = - lazyClassKeyAnnotation.getAsType("value").getTypeElement().getClassName(); - entries.put( - key, - FieldSpec.builder( - TypeNames.STRING, - uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_'))) - // TODO(b/217435141): Leave the field as non-final. We will apply @IdentifierNameString - // on the field, which doesn't work well with static final fields. - .addModifiers(STATIC) - .initializer("$S", lazyClassKey.reflectionName()) - .build()); - // To be able to apply -includedescriptorclasses rule to keep the class names referenced by - // LazyClassKey, we need to generate fields that uses those classes as type in - // LazyClassKeyProvider. For types that are not accessible from the generated component, we - // generate fields in the proxy class. - // Note: the generated field should not be initialized to avoid class loading. - if (isMapKeyAccessibleFrom(lazyClassKeyAnnotation, shardImplementation.name().packageName())) { - keepClassNamesFields.put( - key, + private void addField( + ClassName lazyClassKey, XAnnotation lazyClassKeyAnnotation, String accessingPackage) { + entries.put( + lazyClassKey, FieldSpec.builder( - lazyClassKey, + TypeNames.STRING, uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_'))) - .addAnnotation(TypeNames.KEEP_FIELD_TYPE) + // TODO(b/217435141): Leave the field as non-final. We will apply + // @IdentifierNameString on the field, which doesn't work well with static final + // fields. + .addModifiers(STATIC) + .initializer("$S", lazyClassKey.reflectionName()) .build()); + // To be able to apply -includedescriptorclasses rule to keep the class names referenced by + // LazyClassKey, we need to generate fields that uses those classes as type in + // LazyClassKeyProvider. For types that are not accessible from the generated component, we + // generate fields in the proxy class. + // Note: the generated field should not be initialized to avoid class loading. + if (isMapKeyAccessibleFrom(lazyClassKeyAnnotation, accessingPackage)) { + keepClassNamesFields.put( + lazyClassKey, + FieldSpec.builder( + lazyClassKey, + uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_'))) + .addAnnotation(TypeNames.KEEP_FIELD_TYPE) + .build()); + } + } + + private TypeSpec build() { + return TypeSpec.classBuilder(mapKeyProviderType) + .addAnnotation(TypeNames.IDENTIFIER_NAME_STRING) + .addModifiers(PRIVATE, STATIC, FINAL) + .addFields(entries.values()) + .addFields(keepClassNamesFields.values()) + .build(); + } + } + + public static final String MAP_KEY_PROVIDER_NAME = "LazyClassKeyProvider"; + private final GeneratedImplementation topLevelImplementation; + private final LazyClassKeyProviderCache cache; + + @Inject + LazyClassKeyProviders( + @TopLevel GeneratedImplementation topLevelImplementation, LazyClassKeyProviderCache cache) { + this.topLevelImplementation = topLevelImplementation; + this.cache = cache; + } + + /** Returns a reference to a field in LazyClassKeyProvider that corresponds to this binding. */ + CodeBlock getMapKeyExpression(Key key) { + // This is for avoid generating empty LazyClassKeyProvider. + if (cache.entries.isEmpty()) { + String name = topLevelImplementation.getUniqueClassName(MAP_KEY_PROVIDER_NAME); + cache.mapKeyProviderType = topLevelImplementation.name().nestedClass(name); + topLevelImplementation.addTypeSupplier(this::build); } + return cache.getMapKeyExpression(key); } private TypeSpec build() { - TypeSpec.Builder builder = - TypeSpec.classBuilder(mapKeyProviderType) - .addAnnotation(TypeNames.IDENTIFIER_NAME_STRING) - .addModifiers(PRIVATE, STATIC, FINAL) - .addFields(entries.values()) - .addFields(keepClassNamesFields.values()); - return builder.build(); + return cache.build(); } } diff --git a/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java index 81d91e05d2e..1e186b5cd24 100644 --- a/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java +++ b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java @@ -48,6 +48,7 @@ final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpr @AssistedInject MapFactoryCreationExpression( @Assisted MultiboundMapBinding binding, + LazyClassKeyProviders lazyClassKeyProviders, XProcessingEnv processingEnv, ComponentImplementation componentImplementation, ComponentRequestRepresentations componentRequestRepresentations, @@ -58,8 +59,7 @@ final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpr this.componentImplementation = componentImplementation; this.graph = graph; this.useLazyClassKey = MapKeys.useLazyClassKey(binding, graph); - this.lazyClassKeyProviders = - componentImplementation.shardImplementation(binding).getLazyClassKeyProviders(); + this.lazyClassKeyProviders = lazyClassKeyProviders; } @Override diff --git a/java/dagger/internal/codegen/writing/MapRequestRepresentation.java b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java index 3225e9513a2..ae11ea8c279 100644 --- a/java/dagger/internal/codegen/writing/MapRequestRepresentation.java +++ b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java @@ -61,6 +61,7 @@ final class MapRequestRepresentation extends RequestRepresentation { @AssistedInject MapRequestRepresentation( @Assisted MultiboundMapBinding binding, + LazyClassKeyProviders lazyClassKeyProviders, XProcessingEnv processingEnv, BindingGraph graph, ComponentImplementation componentImplementation, @@ -73,8 +74,7 @@ final class MapRequestRepresentation extends RequestRepresentation { this.dependencies = Maps.toMap(binding.dependencies(), dep -> graph.contributionBinding(dep.key())); this.useLazyClassKey = MapKeys.useLazyClassKey(binding, graph); - this.lazyClassKeyProviders = - componentImplementation.shardImplementation(binding).getLazyClassKeyProviders(); + this.lazyClassKeyProviders = lazyClassKeyProviders; } @Override diff --git a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_DEFAULT_MODE_test.DaggerTestComponent b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_DEFAULT_MODE_test.DaggerTestComponent index 1da9d27f06a..2d8e6fec35b 100644 --- a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_DEFAULT_MODE_test.DaggerTestComponent +++ b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_DEFAULT_MODE_test.DaggerTestComponent @@ -54,19 +54,19 @@ final class DaggerTestComponent { public Map, Integer> classKey() { return LazyClassKeyMap.of(ImmutableMap.of(LazyClassKeyProvider.test_Foo_Bar, MapKeyBindingsModule.classKey(), LazyClassKeyProvider.test_Foo_Bar3, MapKeyBindingsModule.classKey2())); } + } - @IdentifierNameString - private static final class LazyClassKeyProvider { - static String test_Foo_Bar = "test.Foo_Bar"; + @IdentifierNameString + private static final class LazyClassKeyProvider { + static String test_Foo_Bar = "test.Foo_Bar"; - static String test_Foo_Bar3 = "test.Foo$Bar"; + static String test_Foo_Bar3 = "test.Foo$Bar"; - @KeepFieldType - Foo_Bar test_Foo_Bar2; + @KeepFieldType + Foo_Bar test_Foo_Bar2; - @KeepFieldType - Foo.Bar test_Foo_Bar4; - } + @KeepFieldType + Foo.Bar test_Foo_Bar4; } } diff --git a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_FAST_INIT_MODE_test.DaggerTestComponent b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_FAST_INIT_MODE_test.DaggerTestComponent index 1da9d27f06a..2d8e6fec35b 100644 --- a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_FAST_INIT_MODE_test.DaggerTestComponent +++ b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_lazyClassKeySimilarQualifiedName_doesNotConflict_FAST_INIT_MODE_test.DaggerTestComponent @@ -54,19 +54,19 @@ final class DaggerTestComponent { public Map, Integer> classKey() { return LazyClassKeyMap.of(ImmutableMap.of(LazyClassKeyProvider.test_Foo_Bar, MapKeyBindingsModule.classKey(), LazyClassKeyProvider.test_Foo_Bar3, MapKeyBindingsModule.classKey2())); } + } - @IdentifierNameString - private static final class LazyClassKeyProvider { - static String test_Foo_Bar = "test.Foo_Bar"; + @IdentifierNameString + private static final class LazyClassKeyProvider { + static String test_Foo_Bar = "test.Foo_Bar"; - static String test_Foo_Bar3 = "test.Foo$Bar"; + static String test_Foo_Bar3 = "test.Foo$Bar"; - @KeepFieldType - Foo_Bar test_Foo_Bar2; + @KeepFieldType + Foo_Bar test_Foo_Bar2; - @KeepFieldType - Foo.Bar test_Foo_Bar4; - } + @KeepFieldType + Foo.Bar test_Foo_Bar4; } } diff --git a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_DEFAULT_MODE_test.DaggerTestComponent b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_DEFAULT_MODE_test.DaggerTestComponent index 9b115d4257d..a1b4bfe4a1f 100644 --- a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_DEFAULT_MODE_test.DaggerTestComponent +++ b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_DEFAULT_MODE_test.DaggerTestComponent @@ -90,11 +90,11 @@ final class DaggerTestComponent { public javax.inject.Provider> complexKeyProvider() { return mapOfComplexKeyAndIntegerProvider; } + } - @IdentifierNameString - private static final class LazyClassKeyProvider { - static String mapkeys_MapKeys_Inaccessible = "mapkeys.MapKeys$Inaccessible"; - } + @IdentifierNameString + private static final class LazyClassKeyProvider { + static String mapkeys_MapKeys_Inaccessible = "mapkeys.MapKeys$Inaccessible"; } } diff --git a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_FAST_INIT_MODE_test.DaggerTestComponent b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_FAST_INIT_MODE_test.DaggerTestComponent index f017580c5ba..b3c32b90939 100644 --- a/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_FAST_INIT_MODE_test.DaggerTestComponent +++ b/javatests/dagger/internal/codegen/goldens/LazyClassKeyMapBindingComponentProcessorTest_mapBindingsWithInaccessibleKeys_FAST_INIT_MODE_test.DaggerTestComponent @@ -110,11 +110,11 @@ final class DaggerTestComponent { } } } + } - @IdentifierNameString - private static final class LazyClassKeyProvider { - static String mapkeys_MapKeys_Inaccessible = "mapkeys.MapKeys$Inaccessible"; - } + @IdentifierNameString + private static final class LazyClassKeyProvider { + static String mapkeys_MapKeys_Inaccessible = "mapkeys.MapKeys$Inaccessible"; } }