Skip to content

Commit

Permalink
Generate a class for each @LazyClassKey annotated binding, and move f…
Browse files Browse the repository at this point in the history
…ields from `LazyClassKeyProvider` into the generated class.

PiperOrigin-RevId: 688224478
  • Loading branch information
wanyingd1996 authored and Dagger Team committed Oct 21, 2024
1 parent 4cd1daf commit d7cd84b
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 171 deletions.
12 changes: 0 additions & 12 deletions java/dagger/internal/codegen/binding/MapKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@

/** Methods for extracting {@link MapKey} annotations and key code blocks from binding elements. */
public final class MapKeys {
public static final String LAZY_CLASS_KEY_NAME_FIELD = "lazyClassKeyName";
public static final String KEEP_FIELD_TYPE_FIELD = "keepFieldType";

/**
* If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it.
Expand Down Expand Up @@ -219,15 +217,5 @@ public static boolean useLazyClassKey(Binding binding, BindingGraph graph) {
return false;
}

public static CodeBlock getLazyClassMapKeyExpression(ContributionBinding contributionBinding) {
ClassName proxyClassName =
lazyClassKeyProxyClassName(XElements.asMethod(contributionBinding.bindingElement().get()));
return CodeBlock.of("$T.$N", proxyClassName, LAZY_CLASS_KEY_NAME_FIELD);
}

public static ClassName lazyClassKeyProxyClassName(XMethodElement methodElement) {
return elementBasedClassName(methodElement, "_LazyMapKey");
}

private MapKeys() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@

package dagger.internal.codegen.processingstep;

import static androidx.room.compiler.processing.XElementKt.isTypeElement;
import static java.nio.charset.StandardCharsets.UTF_8;

import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XFiler;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.base.Joiner;
Expand All @@ -31,7 +29,6 @@
import com.google.common.collect.SetMultimap;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.writing.LazyMapKeyProxyGenerator;
import dagger.internal.codegen.xprocessing.XElements;
import java.io.BufferedWriter;
import java.io.IOException;
Expand All @@ -47,12 +44,9 @@
final class LazyClassKeyProcessingStep extends TypeCheckingProcessingStep<XElement> {
private static final String PROGUARD_KEEP_RULE = "-keep,allowobfuscation,allowshrinking class ";
private final SetMultimap<ClassName, ClassName> processedElements = LinkedHashMultimap.create();
private final LazyMapKeyProxyGenerator lazyMapKeyProxyGenerator;

@Inject
LazyClassKeyProcessingStep(LazyMapKeyProxyGenerator lazyMapKeyProxyGenerator) {
this.lazyMapKeyProxyGenerator = lazyMapKeyProxyGenerator;
}
LazyClassKeyProcessingStep() {}

@Override
public ImmutableSet<ClassName> annotationClassNames() {
Expand All @@ -67,28 +61,8 @@ protected void process(XElement element, ImmutableSet<ClassName> annotations) {
.getAsType("value")
.getTypeElement()
.getClassName();
// No need to fail, since we want to support customized usage of class key annotations.
// https://github.com/google/dagger/pull/2831
if (!isMapBinding(element) || !isModuleOrProducerModule(element.getEnclosingElement())) {
return;
}
XTypeElement moduleElement = XElements.asTypeElement(element.getEnclosingElement());
processedElements.put(moduleElement.getClassName(), lazyClassKey);
XMethodElement method = XElements.asMethod(element);
lazyMapKeyProxyGenerator.generate(method);
}

private static boolean isMapBinding(XElement element) {
return element.hasAnnotation(TypeNames.INTO_MAP)
&& (element.hasAnnotation(TypeNames.BINDS)
|| element.hasAnnotation(TypeNames.PROVIDES)
|| element.hasAnnotation(TypeNames.PRODUCES));
}

private static boolean isModuleOrProducerModule(XElement element) {
return isTypeElement(element)
&& (element.hasAnnotation(TypeNames.MODULE)
|| element.hasAnnotation(TypeNames.PRODUCER_MODULE));
}

@Override
Expand Down
158 changes: 158 additions & 0 deletions java/dagger/internal/codegen/writing/LazyClassKeyProviders.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright (C) 2024 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.internal.codegen.writing;

import static dagger.internal.codegen.base.MapKeyAccessibility.isMapKeyAccessibleFrom;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.STATIC;

import androidx.room.compiler.processing.XAnnotation;
import com.google.common.base.Preconditions;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.codegen.base.UniqueNameSet;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.Key;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.inject.Inject;

/**
* Keeps track of all providers for DaggerMap keys.
*
* <p>Generated class looks like below:
*
* <pre>{@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;
* }
* }</pre>
*/
public final class LazyClassKeyProviders {
@PerGeneratedFile
static final class LazyClassKeyProviderCache {
// Map key to its corresponding the field reference expression from LazyClassKeyProvider.
final Map<ClassName, CodeBlock> mapKeyToProvider = new LinkedHashMap<>();
private final Map<ClassName, FieldSpec> entries = new LinkedHashMap<>();
private final Map<ClassName, FieldSpec> keepClassNamesFields = new LinkedHashMap<>();
private final UniqueNameSet uniqueFieldNames = new UniqueNameSet();
private ClassName mapKeyProviderType;

@Inject
LazyClassKeyProviderCache() {}

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);
}

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);
}

private void addField(
ClassName lazyClassKey, XAnnotation lazyClassKeyAnnotation, String accessingPackage) {
entries.put(
lazyClassKey,
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, 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() {
return cache.build();
}
}
121 changes: 0 additions & 121 deletions java/dagger/internal/codegen/writing/LazyMapKeyProxyGenerator.java

This file was deleted.

Loading

0 comments on commit d7cd84b

Please sign in to comment.