blob: a2d6e00798768867d5f86742cd1035c10d9bcc45 [file] [log] [blame]
// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.shaking.KeepInfo.Joiner;
import com.android.tools.r8.utils.MapUtils;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
public class MinimumKeepInfoCollection {
private static final MinimumKeepInfoCollection EMPTY =
new MinimumKeepInfoCollection(Collections.emptyMap());
private final Map<DexReference, KeepInfo.Joiner<?, ?, ?>> minimumKeepInfo;
private MinimumKeepInfoCollection(Map<DexReference, KeepInfo.Joiner<?, ?, ?>> minimumKeepInfo) {
this.minimumKeepInfo = minimumKeepInfo;
}
public static MinimumKeepInfoCollection create() {
return new MinimumKeepInfoCollection(new IdentityHashMap<>());
}
public static MinimumKeepInfoCollection create(int capacity) {
return new MinimumKeepInfoCollection(new IdentityHashMap<>(capacity));
}
public static MinimumKeepInfoCollection createConcurrent() {
return new MinimumKeepInfoCollection(new ConcurrentHashMap<>());
}
public static MinimumKeepInfoCollection empty() {
return EMPTY;
}
public void forEach(BiConsumer<DexReference, KeepInfo.Joiner<?, ?, ?>> consumer) {
minimumKeepInfo.forEach(consumer);
}
public void forEach(
DexDefinitionSupplier definitions,
BiConsumer<DexProgramClass, KeepClassInfo.Joiner> classConsumer,
BiConsumer<ProgramField, KeepFieldInfo.Joiner> fieldConsumer,
BiConsumer<ProgramMethod, KeepMethodInfo.Joiner> methodConsumer) {
minimumKeepInfo.forEach(
(reference, joiner) -> {
DexProgramClass contextClass =
asProgramClassOrNull(definitions.definitionFor(reference.getContextType()));
if (contextClass != null) {
reference.accept(
clazz -> classConsumer.accept(contextClass, joiner.asClassJoiner()),
fieldReference -> {
ProgramField field = contextClass.lookupProgramField(fieldReference);
if (field != null) {
fieldConsumer.accept(field, joiner.asFieldJoiner());
}
},
methodReference -> {
ProgramMethod method = contextClass.lookupProgramMethod(methodReference);
if (method != null) {
methodConsumer.accept(method, joiner.asMethodJoiner());
}
});
}
});
}
@SuppressWarnings("unchecked")
public <T extends DexReference> void forEachThatMatches(
BiPredicate<DexReference, Joiner<?, ?, ?>> predicate,
BiConsumer<T, KeepInfo.Joiner<?, ?, ?>> consumer) {
minimumKeepInfo.forEach(
(reference, minimumKeepInfoForReference) -> {
if (predicate.test(reference, minimumKeepInfoForReference)) {
consumer.accept((T) reference, minimumKeepInfoForReference);
}
});
}
public KeepInfo.Joiner<?, ?, ?> getOrDefault(
DexReference reference, KeepInfo.Joiner<?, ?, ?> defaultValue) {
return minimumKeepInfo.getOrDefault(reference, defaultValue);
}
public KeepInfo.Joiner<?, ?, ?> getOrCreateMinimumKeepInfoFor(DexReference reference) {
return minimumKeepInfo.computeIfAbsent(
reference, ignoreKey(() -> KeepInfo.newEmptyJoinerFor(reference)));
}
public boolean hasMinimumKeepInfoThatMatches(
DexReference reference, Predicate<KeepInfo.Joiner<?, ?, ?>> predicate) {
KeepInfo.Joiner<?, ?, ?> minimumKeepInfoForReference = minimumKeepInfo.get(reference);
return minimumKeepInfoForReference != null && predicate.test(minimumKeepInfoForReference);
}
public boolean isEmpty() {
return minimumKeepInfo.isEmpty();
}
public void merge(MinimumKeepInfoCollection otherMinimumKeepInfo) {
otherMinimumKeepInfo.forEach(this::mergeMinimumKeepInfoFor);
}
public void mergeMinimumKeepInfoFor(
DexReference reference, KeepInfo.Joiner<?, ?, ?> minimumKeepInfoForReference) {
getOrCreateMinimumKeepInfoFor(reference).mergeUnsafe(minimumKeepInfoForReference);
}
public void pruneDeadItems(DexDefinitionSupplier definitions, Enqueuer enqueuer) {
MapUtils.removeIf(
minimumKeepInfo,
(reference, minimumKeepInfoForReference) -> {
assert !minimumKeepInfoForReference.isBottom();
ProgramDefinition definition =
reference.apply(
clazz -> asProgramClassOrNull(definitions.definitionFor(clazz)),
field ->
field.lookupOnProgramClass(
asProgramClassOrNull(definitions.definitionFor(field.getHolderType()))),
method ->
method.lookupOnProgramClass(
asProgramClassOrNull(definitions.definitionFor(method.getHolderType()))));
return definition == null || !enqueuer.isReachable(definition);
});
}
public void pruneItems(PrunedItems prunedItems) {
minimumKeepInfo.keySet().removeIf(prunedItems::isRemoved);
}
public KeepClassInfo.Joiner remove(DexType clazz) {
return (KeepClassInfo.Joiner) minimumKeepInfo.remove(clazz);
}
public KeepFieldInfo.Joiner remove(DexField field) {
return (KeepFieldInfo.Joiner) minimumKeepInfo.remove(field);
}
public KeepMethodInfo.Joiner remove(DexMethod method) {
return (KeepMethodInfo.Joiner) minimumKeepInfo.remove(method);
}
public MinimumKeepInfoCollection rewrittenWithLens(GraphLens graphLens) {
MinimumKeepInfoCollection rewrittenMinimumKeepInfo = create(size());
forEach(
(reference, minimumKeepInfoForReference) -> {
DexReference rewrittenReference =
reference.apply(
type -> {
DexType rewrittenType = graphLens.lookupType(type);
if (rewrittenType.isPrimitiveType()) {
// May happen due to enum unboxing.
assert type.isClassType();
assert rewrittenType.isIntType();
return null;
}
return rewrittenType;
},
graphLens::getRenamedFieldSignature,
graphLens::getRenamedMethodSignature);
if (rewrittenReference != null) {
rewrittenMinimumKeepInfo
.getOrCreateMinimumKeepInfoFor(rewrittenReference)
.mergeUnsafe(minimumKeepInfoForReference);
}
});
return rewrittenMinimumKeepInfo;
}
public int size() {
return minimumKeepInfo.size();
}
}