• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.writing;
18 
19 import static dagger.internal.codegen.base.MapKeyAccessibility.isMapKeyAccessibleFrom;
20 import static javax.lang.model.element.Modifier.FINAL;
21 import static javax.lang.model.element.Modifier.PRIVATE;
22 import static javax.lang.model.element.Modifier.STATIC;
23 
24 import androidx.room.compiler.processing.XAnnotation;
25 import com.google.common.base.Preconditions;
26 import com.squareup.javapoet.ClassName;
27 import com.squareup.javapoet.CodeBlock;
28 import com.squareup.javapoet.FieldSpec;
29 import com.squareup.javapoet.TypeSpec;
30 import dagger.internal.codegen.base.UniqueNameSet;
31 import dagger.internal.codegen.javapoet.TypeNames;
32 import dagger.internal.codegen.model.Key;
33 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
34 import java.util.HashMap;
35 import java.util.Map;
36 
37 /** Keeps track of all providers for DaggerMap keys. */
38 public final class LazyClassKeyProviders {
39   public static final String MAP_KEY_PROVIDER_NAME = "LazyClassKeyProvider";
40   private final ClassName mapKeyProviderType;
41   private final Map<Key, FieldSpec> entries = new HashMap<>();
42   private final Map<Key, FieldSpec> keepClassNamesFields = new HashMap<>();
43   private final UniqueNameSet uniqueFieldNames = new UniqueNameSet();
44   private final ShardImplementation shardImplementation;
45   private boolean providerAdded = false;
46 
LazyClassKeyProviders(ShardImplementation shardImplementation)47   LazyClassKeyProviders(ShardImplementation shardImplementation) {
48     String name = shardImplementation.getUniqueClassName(MAP_KEY_PROVIDER_NAME);
49     mapKeyProviderType = shardImplementation.name().nestedClass(name);
50     this.shardImplementation = shardImplementation;
51   }
52 
53   /** Returns a reference to a field in LazyClassKeyProvider that corresponds to this binding. */
getMapKeyExpression(Key key)54   CodeBlock getMapKeyExpression(Key key) {
55     // This is for avoid generating empty LazyClassKeyProvider in codegen tests
56     if (!providerAdded) {
57       shardImplementation.addTypeSupplier(this::build);
58       providerAdded = true;
59     }
60     if (!entries.containsKey(key)) {
61       addField(key);
62     }
63     return CodeBlock.of("$T.$N", mapKeyProviderType, entries.get(key));
64   }
65 
addField(Key key)66   private void addField(Key key) {
67     Preconditions.checkArgument(
68         key.multibindingContributionIdentifier().isPresent()
69             && key.multibindingContributionIdentifier()
70                 .get()
71                 .bindingMethod()
72                 .xprocessing()
73                 .hasAnnotation(TypeNames.LAZY_CLASS_KEY));
74     XAnnotation lazyClassKeyAnnotation =
75         key.multibindingContributionIdentifier()
76             .get()
77             .bindingMethod()
78             .xprocessing()
79             .getAnnotation(TypeNames.LAZY_CLASS_KEY);
80     ClassName lazyClassKey =
81         lazyClassKeyAnnotation.getAsType("value").getTypeElement().getClassName();
82     entries.put(
83         key,
84         FieldSpec.builder(
85                 TypeNames.STRING,
86                 uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_')))
87             // TODO(b/217435141): Leave the field as non-final. We will apply @IdentifierNameString
88             // on the field, which doesn't work well with static final fields.
89             .addModifiers(STATIC)
90             .initializer("$S", lazyClassKey.reflectionName())
91             .build());
92     // To be able to apply -includedescriptorclasses rule to keep the class names referenced by
93     // LazyClassKey, we need to generate fields that uses those classes as type in
94     // LazyClassKeyProvider. For types that are not accessible from the generated component, we
95     // generate fields in the proxy class.
96     // Note: the generated field should not be initialized to avoid class loading.
97     if (isMapKeyAccessibleFrom(lazyClassKeyAnnotation, shardImplementation.name().packageName())) {
98       keepClassNamesFields.put(
99           key,
100           FieldSpec.builder(
101                   lazyClassKey,
102                   uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_')))
103               .addAnnotation(TypeNames.KEEP_FIELD_TYPE)
104               .build());
105     }
106   }
107 
build()108   private TypeSpec build() {
109     TypeSpec.Builder builder =
110         TypeSpec.classBuilder(mapKeyProviderType)
111             .addAnnotation(TypeNames.IDENTIFIER_NAME_STRING)
112             .addModifiers(PRIVATE, STATIC, FINAL)
113             .addFields(entries.values())
114             .addFields(keepClassNamesFields.values());
115     return builder.build();
116   }
117 }
118