|  | // 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<?, ?, ?> 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 = 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; | 
|  | } | 
|  | } |