Revert "Reapply "Move if rule evalation logic to own class""
This reverts commit 867a328fed771c64aa76b338e603f8b41ca5cbcf.
Revert "Reapply "Introduce method to evaluate if-rule on a single class""
This reverts commit bedce3dbbf7f9926d2c350f5a3e4cd95bdb9172f.
Revert "Reapply "Cleanup if rule evaluator by removing ifRules field and timing""
This reverts commit 6d8adb69c895a76731014e30ecc32fb41c87cf95.
Revert "Reapply "Avoid computing effectively live info in each if rule evaluation""
This reverts commit be24b59a0414b6207402b25dd28597e136a42d24.
Revert "Reapply "Rewrite if rule evaluator to enqueuer extension""
This reverts commit e5bd7dd0abc10c053b0fffbdd853e278af9243f7.
Revert "Fix NPE in IfRuleEvaluatorFactory"
This reverts commit 563b7722e1db855cc83c75d26621369694a167b4.
Revert "Remove new assert in Enqueuer"
This reverts commit 1d45faa103a1641f7ad273b45604aedd8180a749.
Change-Id: I683c0c787a49cf3f5e88725899014c47a9bdf72f
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 6e97625..fa22d3a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -147,6 +147,7 @@
import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
import com.android.tools.r8.shaking.KeepReason.ReflectiveUseFromXml;
import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
+import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSetBuilder;
import com.android.tools.r8.shaking.RootSetUtils.RootSet;
import com.android.tools.r8.shaking.RootSetUtils.RootSetBase;
import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
@@ -171,6 +172,7 @@
import com.android.tools.r8.utils.collections.ProgramFieldSet;
import com.android.tools.r8.utils.collections.ProgramMethodMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -458,6 +460,9 @@
private final Map<DexType, Map<DexAnnotation, List<ProgramDefinition>>>
deferredParameterAnnotations = new IdentityHashMap<>();
+ /** Map of active if rules to speed up aapt2 generated keep rules. */
+ private Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules;
+
/**
* A cache of ScopedDexMethodSet for each live type used for determining that virtual methods that
* cannot be removed because they are widening access for another virtual method defined earlier
@@ -533,7 +538,6 @@
ResourceAccessAnalysis.register(appView, this);
CovariantReturnTypeEnqueuerExtension.register(appView, this);
}
- IfRuleEvaluatorFactory.register(appView, this, executorService);
targetedMethods = new LiveMethodsSet(graphReporter::registerMethod);
failedClassResolutionTargets = SetUtils.newIdentityHashSet(0);
@@ -830,10 +834,6 @@
return keepInfo.getClassInfo(clazz);
}
- public SubtypingInfo getSubtypingInfo() {
- return subtypingInfo;
- }
-
public boolean hasMinimumKeepInfoThatMatches(
DexProgramClass clazz, Predicate<KeepClassInfo.Joiner> predicate) {
MinimumKeepInfoCollection minimumKeepInfoCollection =
@@ -3534,6 +3534,29 @@
return liveTypes.contains(clazz);
}
+ public boolean isEffectivelyLive(DexProgramClass clazz) {
+ if (isTypeLive(clazz)) {
+ return true;
+ }
+ if (mode.isInitialTreeShaking()) {
+ return false;
+ }
+ // TODO(b/325014359): Replace this by value tracking in instructions (akin to resource values).
+ for (DexEncodedField field : clazz.fields()) {
+ if (field.getOptimizationInfo().valueHasBeenPropagated()) {
+ return true;
+ }
+ }
+ // TODO(b/325014359): Replace this by value or position tracking.
+ // We need to be careful not to throw away such values/positions.
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.getOptimizationInfo().returnValueHasBeenPropagated()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public boolean isOriginalReferenceEffectivelyLive(DexReference reference) {
// The effectively-live original set contains types, fields and methods witnessed by
// instructions, such as method inlining positions.
@@ -4733,6 +4756,28 @@
long numberOfLiveItemsAfterProcessing = getNumberOfLiveItems();
if (numberOfLiveItemsAfterProcessing > numberOfLiveItems) {
timing.time("Conditional rules", () -> applicableRules.evaluateConditionalRules(this));
+
+ // Build the mapping of active if rules. We use a single collection of if-rules to allow
+ // removing if rules that have a constant sequent keep rule when they materialize.
+ if (activeIfRules == null) {
+ activeIfRules = new HashMap<>();
+ IfRuleClassPartEquivalence equivalence = new IfRuleClassPartEquivalence();
+ for (ProguardIfRule ifRule : rootSet.ifRules) {
+ Wrapper<ProguardIfRule> wrap = equivalence.wrap(ifRule);
+ activeIfRules.computeIfAbsent(wrap, ignore -> new LinkedHashSet<>()).add(ifRule);
+ }
+ }
+ ConsequentRootSetBuilder consequentSetBuilder =
+ ConsequentRootSet.builder(appView, this, subtypingInfo);
+ IfRuleEvaluator ifRuleEvaluator =
+ new IfRuleEvaluator(
+ appView,
+ subtypingInfo,
+ this,
+ executorService,
+ activeIfRules,
+ consequentSetBuilder);
+ addConsequentRootSet(ifRuleEvaluator.run());
assert getNumberOfLiveItems() == numberOfLiveItemsAfterProcessing;
if (!worklist.isEmpty()) {
continue;
@@ -4852,7 +4897,7 @@
}
}
- public long getNumberOfLiveItems() {
+ private long getNumberOfLiveItems() {
long result = liveTypes.getItems().size();
result += liveMethods.items.size();
result += liveFields.fields.size();
@@ -4860,7 +4905,7 @@
return result;
}
- void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
+ private void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
// TODO(b/132600955): This modifies the root set, but the consequent should not be persistent.
// Instead, the consequent root set should be added to collections that are owned by the
// enqueuer, similar to Enqueuer#dependentMinimumKeepClassInfo.
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 4841933..44ba57d 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -14,20 +14,22 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.InlineRule.InlineRuleType;
+import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSetBuilder;
import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
import com.android.tools.r8.threading.TaskCollection;
-import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions.ProguardIfRuleEvaluationData;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
+import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
public class IfRuleEvaluator {
@@ -35,6 +37,7 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final SubtypingInfo subtypingInfo;
private final Enqueuer enqueuer;
+ private final Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRules;
private final ConsequentRootSetBuilder rootSetBuilder;
private final TaskCollection<?> tasks;
@@ -42,24 +45,29 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
Enqueuer enqueuer,
- ConsequentRootSetBuilder rootSetBuilder,
- TaskCollection<?> tasks) {
- assert tasks.isEmpty();
+ ExecutorService executorService,
+ Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRules,
+ ConsequentRootSetBuilder rootSetBuilder) {
this.appView = appView;
this.subtypingInfo = subtypingInfo;
this.enqueuer = enqueuer;
+ this.ifRules = ifRules;
this.rootSetBuilder = rootSetBuilder;
- this.tasks = tasks;
+ this.tasks = new TaskCollection<>(appView.options(), executorService);
}
- public void run(
- Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRules,
- Set<DexProgramClass> effectivelyFakeLiveClasses)
- throws ExecutionException {
- MapUtils.removeIf(
- ifRules,
- (ifRuleWrapper, ifRulesInEquivalence) -> {
- ProguardIfRule ifRuleKey = ifRuleWrapper.get();
+ public ConsequentRootSet run() throws ExecutionException {
+ appView.appInfo().app().timing.begin("Find consequent items for -if rules...");
+ try {
+ if (ifRules != null && !ifRules.isEmpty()) {
+ Iterator<Map.Entry<Wrapper<ProguardIfRule>, Set<ProguardIfRule>>> it =
+ ifRules.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRuleEntry = it.next();
+ ProguardIfRule ifRuleKey = ifRuleEntry.getKey().get();
+ Set<ProguardIfRule> ifRulesInEquivalence = ifRuleEntry.getValue();
+ ProguardIfRuleEvaluationData ifRuleEvaluationData =
+ appView.options().testing.proguardIfRuleEvaluationData;
List<ProguardIfRule> toRemove = new ArrayList<>();
// Depending on which types that trigger the -if rule, the application of the subsequent
@@ -68,62 +76,42 @@
for (DexProgramClass clazz :
ifRuleKey.relevantCandidatesForRule(
appView, subtypingInfo, appView.appInfo().classes())) {
- if (!isEffectivelyLive(clazz, effectivelyFakeLiveClasses)) {
+ if (!isEffectivelyLive(clazz)) {
continue;
}
- evaluateRuleOnEffectivelyLiveClass(
- ifRuleKey,
- ifRulesInEquivalence,
- clazz,
- matchedIfRule -> {
- if (canRemoveSubsequentKeepRule(matchedIfRule)) {
- toRemove.add(matchedIfRule);
- }
- });
+
+ // Check if the class matches the if-rule.
+ if (appView.options().testing.measureProguardIfRuleEvaluations) {
+ ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
+ }
+ if (evaluateClassForIfRule(ifRuleKey, clazz)) {
+ // When matching an if rule against a type, the if-rule are filled with the current
+ // capture of wildcards. Propagate this down to member rules with same class part
+ // equivalence.
+ for (ProguardIfRule ifRule : ifRulesInEquivalence) {
+ registerClassCapture(ifRule, clazz, clazz);
+ if (appView.options().testing.measureProguardIfRuleEvaluations) {
+ ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
+ }
+ boolean matched = evaluateIfRuleMembersAndMaterialize(ifRule, clazz);
+ if (matched && canRemoveSubsequentKeepRule(ifRule)) {
+ toRemove.add(ifRule);
+ }
+ }
+ }
}
if (ifRulesInEquivalence.size() == toRemove.size()) {
- return true;
+ it.remove();
+ } else if (!toRemove.isEmpty()) {
+ ifRulesInEquivalence.removeAll(toRemove);
}
- toRemove.forEach(ifRulesInEquivalence::remove);
- return false;
- });
- tasks.await();
- }
-
- private boolean isEffectivelyLive(
- DexProgramClass clazz, Set<DexProgramClass> effectivelyFakeLiveClasses) {
- return enqueuer.isTypeLive(clazz) || effectivelyFakeLiveClasses.contains(clazz);
- }
-
- private void evaluateRuleOnEffectivelyLiveClass(
- ProguardIfRule ifRuleKey,
- Set<ProguardIfRule> ifRulesInEquivalence,
- DexProgramClass clazz,
- Consumer<ProguardIfRule> matchedConsumer)
- throws ExecutionException {
- // Check if the class matches the if-rule.
- if (!evaluateClassForIfRule(ifRuleKey, clazz)) {
- return;
+ }
+ tasks.await();
+ }
+ } finally {
+ appView.appInfo().app().timing.end();
}
- // When matching an if rule against a type, the if-rule are filled with the current
- // capture of wildcards. Propagate this down to member rules with same class part
- // equivalence.
- for (ProguardIfRule ifRule : ifRulesInEquivalence) {
- registerClassCapture(ifRule, clazz, clazz);
- evaluateIfRuleMembersAndMaterialize(ifRule, clazz, matchedConsumer);
- }
- }
-
- private void incrementNumberOfProguardIfRuleClassEvaluations() {
- if (appView.testing().measureProguardIfRuleEvaluations) {
- appView.testing().proguardIfRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
- }
- }
-
- private void incrementNumberOfProguardIfRuleMemberEvaluations() {
- if (appView.testing().measureProguardIfRuleEvaluations) {
- appView.testing().proguardIfRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
- }
+ return rootSetBuilder.buildConsequentRootSet();
}
private boolean canRemoveSubsequentKeepRule(ProguardIfRule rule) {
@@ -147,9 +135,12 @@
}
}
+ private boolean isEffectivelyLive(DexProgramClass clazz) {
+ return enqueuer.isEffectivelyLive(clazz);
+ }
+
/** Determines if {@param clazz} satisfies the given if-rule class specification. */
private boolean evaluateClassForIfRule(ProguardIfRule rule, DexProgramClass clazz) {
- incrementNumberOfProguardIfRuleClassEvaluations();
if (!RootSetBuilder.satisfyClassType(rule, clazz)) {
return false;
}
@@ -171,15 +162,12 @@
return true;
}
- private void evaluateIfRuleMembersAndMaterialize(
- ProguardIfRule rule, DexProgramClass clazz, Consumer<ProguardIfRule> matchedConsumer)
+ private boolean evaluateIfRuleMembersAndMaterialize(ProguardIfRule rule, DexProgramClass clazz)
throws ExecutionException {
- incrementNumberOfProguardIfRuleMemberEvaluations();
Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
if (memberKeepRules.isEmpty()) {
materializeIfRule(rule, clazz);
- matchedConsumer.accept(rule);
- return;
+ return true;
}
List<DexClassAndField> fieldsInlinedByJavaC = new ArrayList<>();
@@ -235,7 +223,7 @@
// If the number of member rules to hold is more than live members, we can't make it.
if (filteredMembers.size() < memberKeepRules.size()) {
- return;
+ return false;
}
// Depending on which members trigger the -if rule, the application of the subsequent
@@ -265,11 +253,11 @@
if (satisfied) {
materializeIfRule(rule, clazz);
if (canRemoveSubsequentKeepRule(rule)) {
- matchedConsumer.accept(rule);
- return;
+ return true;
}
}
}
+ return false;
}
private boolean isFieldInlinedByJavaC(DexEncodedField field) {
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
deleted file mode 100644
index 1f316e2..0000000
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (c) 2024, 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.utils.MapUtils.ignoreKey;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
-import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
-import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSetBuilder;
-import com.android.tools.r8.threading.TaskCollection;
-import com.android.tools.r8.utils.Timing;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-
-public class IfRuleEvaluatorFactory extends EnqueuerAnalysis {
-
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
-
- /** Map of active if rules. This is important for speeding up aapt2 generated keep rules. */
- private final Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules;
-
- private final Set<DexProgramClass> effectivelyFakeLiveClasses;
- private final Set<DexProgramClass> newlyLiveClasses = Sets.newIdentityHashSet();
- private long previousNumberOfLiveItems = 0;
-
- private final TaskCollection<?> tasks;
-
- public IfRuleEvaluatorFactory(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- Enqueuer enqueuer,
- ExecutorService executorService) {
- this.appView = appView;
- this.activeIfRules = createActiveIfRules(appView.rootSet().ifRules);
- this.effectivelyFakeLiveClasses = createEffectivelyFakeLiveClasses(appView, enqueuer);
- this.tasks = new TaskCollection<>(appView.options(), executorService);
- }
-
- public static void register(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- Enqueuer enqueuer,
- ExecutorService executorService) {
- Set<ProguardIfRule> ifRules =
- appView.hasRootSet() ? appView.rootSet().ifRules : Collections.emptySet();
- if (ifRules != null && !ifRules.isEmpty()) {
- enqueuer.registerAnalysis(new IfRuleEvaluatorFactory(appView, enqueuer, executorService));
- }
- }
-
- @SuppressWarnings("MixedMutabilityReturnType")
- private static Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> createActiveIfRules(
- Set<ProguardIfRule> ifRules) {
- if (ifRules == null || ifRules.isEmpty()) {
- return Collections.emptyMap();
- }
- // Build the mapping of active if rules. We use a single collection of if-rules to allow
- // removing if rules that have a constant sequent keep rule when they materialize.
- Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules = new HashMap<>(ifRules.size());
- IfRuleClassPartEquivalence equivalence = new IfRuleClassPartEquivalence();
- for (ProguardIfRule ifRule : ifRules) {
- Wrapper<ProguardIfRule> wrap = equivalence.wrap(ifRule);
- activeIfRules.computeIfAbsent(wrap, ignoreKey(LinkedHashSet::new)).add(ifRule);
- }
- return activeIfRules;
- }
-
- @SuppressWarnings("MixedMutabilityReturnType")
- private static Set<DexProgramClass> createEffectivelyFakeLiveClasses(
- AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
- if (enqueuer.getMode().isInitialTreeShaking()) {
- return Collections.emptySet();
- }
- Set<DexProgramClass> effectivelyFakeLiveClasses = Sets.newIdentityHashSet();
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (isFakeEffectiveLive(clazz)) {
- effectivelyFakeLiveClasses.add(clazz);
- }
- }
- return effectivelyFakeLiveClasses;
- }
-
- private static boolean isFakeEffectiveLive(DexProgramClass clazz) {
- // TODO(b/325014359): Replace this by value tracking in instructions (akin to resource values).
- for (DexEncodedField field : clazz.fields()) {
- if (field.getOptimizationInfo().valueHasBeenPropagated()) {
- return true;
- }
- }
- // TODO(b/325014359): Replace this by value or position tracking.
- // We need to be careful not to throw away such values/positions.
- for (DexEncodedMethod method : clazz.methods()) {
- if (method.getOptimizationInfo().returnValueHasBeenPropagated()) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void notifyFixpoint(
- Enqueuer enqueuer, EnqueuerWorklist worklist, ExecutorService executorService, Timing timing)
- throws ExecutionException {
- // TODO(b/206086945): Leverage newlyLiveClasses.
- if (activeIfRules.isEmpty()) {
- return;
- }
- long numberOfLiveItems = enqueuer.getNumberOfLiveItems();
- if (numberOfLiveItems == previousNumberOfLiveItems) {
- return;
- }
- SubtypingInfo subtypingInfo = enqueuer.getSubtypingInfo();
- ConsequentRootSetBuilder consequentRootSetBuilder =
- ConsequentRootSet.builder(appView, enqueuer, subtypingInfo);
- IfRuleEvaluator evaluator =
- new IfRuleEvaluator(appView, subtypingInfo, enqueuer, consequentRootSetBuilder, tasks);
- timing.time(
- "Find consequent items for -if rules...",
- () -> evaluator.run(activeIfRules, effectivelyFakeLiveClasses));
- enqueuer.addConsequentRootSet(consequentRootSetBuilder.buildConsequentRootSet());
- previousNumberOfLiveItems = numberOfLiveItems;
- }
-
- @Override
- public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {
- if (effectivelyFakeLiveClasses.contains(clazz)) {
- return;
- }
- newlyLiveClasses.add(clazz);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index ac5d7f6..c991a7d 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -10,11 +10,11 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import java.util.Collections;
import java.util.IdentityHashMap;
-import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
@@ -80,15 +80,8 @@
map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
}
- public static <K, V, E extends Throwable> void removeIf(
- Map<K, V> map, ThrowingBiPredicate<K, V, E> predicate) throws E {
- Iterator<Entry<K, V>> iterator = map.entrySet().iterator();
- while (iterator.hasNext()) {
- Entry<K, V> entry = iterator.next();
- if (predicate.test(entry.getKey(), entry.getValue())) {
- iterator.remove();
- }
- }
+ public static <K, V> void removeIf(Map<K, V> map, BiPredicate<K, V> predicate) {
+ map.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
}
public static <K, V> V removeOrDefault(Map<K, V> map, K key, V defaultValue) {
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingBiPredicate.java b/src/main/java/com/android/tools/r8/utils/ThrowingBiPredicate.java
deleted file mode 100644
index 7e6fa66..0000000
--- a/src/main/java/com/android/tools/r8/utils/ThrowingBiPredicate.java
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) 2024, 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.utils;
-
-public interface ThrowingBiPredicate<S, T, E extends Throwable> {
-
- boolean test(S s, T t) throws E;
-}