Skip to content

Commit

Permalink
Include proguard rules in dagger core artifact so that LazyClassKeyMa…
Browse files Browse the repository at this point in the history
…p's string keys can be obfuscated.

RELNOTES=n/a
PiperOrigin-RevId: 596801641
  • Loading branch information
wanyingd1996 authored and Dagger Team committed Jan 25, 2024
1 parent e71de27 commit 7823446
Show file tree
Hide file tree
Showing 57 changed files with 2,295 additions and 30 deletions.
25 changes: 11 additions & 14 deletions java/dagger/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ load(
"JAVA_RELEASE_MIN",
"POM_VERSION",
)
load("//tools:maven.bzl", "pom_file")
load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
load("//tools:maven.bzl", "gen_maven_artifact")

package(default_visibility = ["//:src"])

Expand All @@ -38,22 +37,20 @@ java_library(
],
)

pom_file(
name = "pom",
artifact_id = "dagger",
gen_maven_artifact(
name = "artifact",
artifact_coordinates = "com.google.dagger:dagger:" + POM_VERSION,
artifact_name = "Dagger",
targets = [":core"],
artifact_target = ":core",
artifact_target_maven_deps = [
"javax.inject:javax.inject",
],
javadoc_root_packages = ["dagger"],
javadoc_srcs = [":javadoc-srcs"],
r8_specs = [":r8.pro"],
)

filegroup(
name = "javadoc-srcs",
srcs = glob(["**/*"]),
)

javadoc_library(
name = "core-javadoc",
srcs = [":javadoc-srcs"],
exclude_packages = ["dagger.internal"],
root_packages = ["dagger"],
deps = ["//third_party/java/jsr330_inject"],
)
131 changes: 131 additions & 0 deletions java/dagger/internal/LazyClassKeyMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* 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;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

/**
* A class keyed map that delegates to a string keyed map under the hood.
*
* <p>A {@code LazyClassKeyMap} is created for @LazyClassKey contributed map binding.
*/
public final class LazyClassKeyMap<V> implements Map<Class<?>, V> {
private final Map<String, V> delegate;

public static <V> Map<Class<?>, V> of(Map<String, V> delegate) {
return new LazyClassKeyMap<>(delegate);
}

private LazyClassKeyMap(Map<String, V> delegate) {
this.delegate = delegate;
}

@Override
public V get(Object key) {
if (!(key instanceof Class)) {
throw new IllegalArgumentException("Key must be a class");
}
return delegate.get(((Class<?>) key).getName());
}

@Override
public Set<Class<?>> keySet() {
// This method will load all class keys, therefore no need to use @LazyClassKey annotated
// bindings.
throw new UnsupportedOperationException(
"Maps created with @LazyClassKey do not support usage of keySet(). Consider @ClassKey"
+ " instead.");
}

@Override
public Collection<V> values() {
return delegate.values();
}

@Override
public boolean isEmpty() {
return delegate.isEmpty();
}

@Override
public boolean containsKey(Object key) {
if (!(key instanceof Class)) {
throw new IllegalArgumentException("Key must be a class");
}
return delegate.containsKey(((Class<?>) key).getName());
}

@Override
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}

@Override
public int size() {
return delegate.size();
}

@Override
public Set<Map.Entry<Class<?>, V>> entrySet() {
// This method will load all class keys, therefore no need to use @LazyClassKey annotated
// bindings.
throw new UnsupportedOperationException(
"Maps created with @LazyClassKey do not support usage of entrySet(). Consider @ClassKey"
+ " instead.");
}

// The dagger map binding should be a immutable map.
@Override
public V remove(Object key) {
throw new UnsupportedOperationException("Dagger map bindings are immutable");
}

@Override
public void clear() {
throw new UnsupportedOperationException("Dagger map bindings are immutable");
}

@Override
public V put(Class<?> key, V value) {
throw new UnsupportedOperationException("Dagger map bindings are immutable");
}

@Override
public void putAll(Map<? extends Class<?>, ? extends V> map) {
throw new UnsupportedOperationException("Dagger map bindings are immutable");
}

/** A factory for {@code LazyClassKeyMap}. */
public static class Factory<V> implements Provider<Map<Class<?>, V>> {
MapFactory<String, V> delegate;

public static <V> Factory<V> of(MapFactory<String, V> delegate) {
return new Factory<>(delegate);
}

private Factory(MapFactory<String, V> delegate) {
this.delegate = delegate;
}

@Override
public Map<Class<?>, V> get() {
return LazyClassKeyMap.of(delegate.get());
}
}
}
21 changes: 21 additions & 0 deletions java/dagger/internal/codegen/binding/MapKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,26 @@ public static Optional<MethodSpec> mapKeyFactoryMethod(
.build());
}

/**
* Returns if this binding is a map binding and uses @LazyClassKey for contributing class keys.
*
* <p>@LazyClassKey won't co-exist with @ClassKey in the graph, since the same binding type cannot
* use more than one @MapKey annotation type and Dagger validation will fail.
*/
public static boolean useLazyClassKey(Binding binding, BindingGraph graph) {
if (!binding.dependencies().isEmpty()) {
ContributionBinding contributionBinding =
graph.contributionBinding(binding.dependencies().iterator().next().key());
return contributionBinding.mapKey().isPresent()
&& contributionBinding
.mapKey()
.get()
.xprocessing()
.getClassName()
.equals(TypeNames.LAZY_CLASS_KEY);
}
return false;
}

private MapKeys() {}
}
6 changes: 6 additions & 0 deletions java/dagger/internal/codegen/javapoet/TypeNames.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ public final class TypeNames {
public static final ClassName SUBCOMPONENT_FACTORY = SUBCOMPONENT.nestedClass("Factory");

// Dagger Internal classnames
public static final ClassName LAZY_CLASS_KEY =
ClassName.get("dagger.multibindings", "LazyClassKey");
public static final ClassName LAZY_CLASS_KEY_MAP =
ClassName.get("dagger.internal", "LazyClassKeyMap");
public static final ClassName LAZY_CLASS_KEY_MAP_FACTORY =
ClassName.get("dagger.internal", "LazyClassKeyMap", "Factory");

public static final ClassName DELEGATE_FACTORY =
ClassName.get("dagger.internal", "DelegateFactory");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ public final class ShardImplementation implements GeneratedImplementation {
private final UniqueNameSet assistedParamNames = new UniqueNameSet();
private final List<CodeBlock> initializations = new ArrayList<>();
private final SwitchingProviders switchingProviders;
private final LazyClassKeyProviders lazyClassKeyProviders;
private final Map<Key, CodeBlock> cancellations = new LinkedHashMap<>();
private final Map<XVariableElement, String> uniqueAssistedName = new LinkedHashMap<>();
private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
Expand All @@ -472,6 +473,7 @@ 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);
}
Expand Down Expand Up @@ -505,6 +507,10 @@ public SwitchingProviders getSwitchingProviders() {
return switchingProviders;
}

public LazyClassKeyProviders getLazyClassKeyProviders() {
return lazyClassKeyProviders;
}

/** Returns the {@link ComponentImplementation} that owns this shard. */
public ComponentImplementation getComponentImplementation() {
return ComponentImplementation.this;
Expand Down
99 changes: 99 additions & 0 deletions java/dagger/internal/codegen/writing/LazyClassKeyProviders.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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 javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.STATIC;

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 dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
import java.util.HashMap;
import java.util.Map;

/** Keeps track of all providers for DaggerMap keys. */
public final class LazyClassKeyProviders {
public static final String MAP_KEY_PROVIDER_NAME = "LazyClassKeyProvider";
private final ClassName mapKeyProviderType;
private final Map<Key, FieldSpec> entries = new HashMap<>();
private final UniqueNameSet uniqueFieldNames = new UniqueNameSet();
private final ShardImplementation shardImplementation;
private boolean providerAdded = false;

LazyClassKeyProviders(ShardImplementation shardImplementation) {
String name = shardImplementation.getUniqueClassName(MAP_KEY_PROVIDER_NAME);
mapKeyProviderType = shardImplementation.name().nestedClass(name);
this.shardImplementation = shardImplementation;
}

/** 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;
}
if (!entries.containsKey(key)) {
addField(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));
ClassName lazyClassKey =
key.multibindingContributionIdentifier()
.get()
.bindingMethod()
.xprocessing()
.getAnnotation(TypeNames.LAZY_CLASS_KEY)
.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());
}

private TypeSpec build() {
TypeSpec.Builder builder =
TypeSpec.classBuilder(mapKeyProviderType)
.addModifiers(PRIVATE, STATIC, FINAL)
.addFields(entries.values());
return builder.build();
}
}
Loading

0 comments on commit 7823446

Please sign in to comment.