| // 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.GraphLens; |
| 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.shaking.KeepInfo.Joiner; |
| import com.android.tools.r8.utils.MapUtils; |
| import java.util.Collections; |
| 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; |
| |
| public MinimumKeepInfoCollection() { |
| this(new ConcurrentHashMap<>()); |
| } |
| |
| private MinimumKeepInfoCollection(Map<DexReference, KeepInfo.Joiner<?, ?, ?>> minimumKeepInfo) { |
| this.minimumKeepInfo = minimumKeepInfo; |
| } |
| |
| 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<?, ?, ?> 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 = new MinimumKeepInfoCollection(); |
| 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; |
| } |
| } |