blob: ba7a84c88da37ae1e67ad437d85a785845c4a560 [file] [log] [blame]
// 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.ifrules;
import com.android.tools.r8.shaking.ProguardClassNameList;
import com.android.tools.r8.shaking.ProguardIfRule;
import com.android.tools.r8.shaking.ProguardIfRulePreconditionMatch;
import com.android.tools.r8.shaking.ProguardKeepRule;
import com.android.tools.r8.shaking.ProguardMemberRule;
import com.android.tools.r8.shaking.ProguardTypeMatcher;
import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificTypes;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class MaterializedSubsequentRulesOptimizer {
public static List<Pair<ProguardIfRulePreconditionMatch, ProguardKeepRule>> optimize(
ProguardIfRule ifRule,
List<Pair<ProguardIfRulePreconditionMatch, ProguardKeepRule>> materializedSubsequentRules) {
if (materializedSubsequentRules.size() <= 1) {
// Nothing to optimize.
return materializedSubsequentRules;
}
if (IterableUtils.hasSize(ifRule.getSubsequentRule().getBackReferences(), 1)) {
return optimizeMaterializedSubsequentRulesWithSingleBackReference(
ifRule, materializedSubsequentRules);
}
return materializedSubsequentRules;
}
private static List<Pair<ProguardIfRulePreconditionMatch, ProguardKeepRule>>
optimizeMaterializedSubsequentRulesWithSingleBackReference(
ProguardIfRule ifRule,
List<Pair<ProguardIfRulePreconditionMatch, ProguardKeepRule>>
materializedSubsequentRules) {
ProguardKeepRule representativeKeepRule =
IterableUtils.first(materializedSubsequentRules).getSecond();
// Back references may occur in the following five elements only.
List<ProguardTypeMatcher> classAnnotations = representativeKeepRule.getClassAnnotations();
ProguardClassNameList classNames = representativeKeepRule.getClassNames();
List<ProguardTypeMatcher> inheritanceAnnotations =
representativeKeepRule.getInheritanceAnnotations();
ProguardTypeMatcher inheritanceClassName = representativeKeepRule.getInheritanceClassName();
int memberRuleWithBackReferenceIndex =
Iterables.indexOf(
ifRule.getSubsequentRule().getMemberRules(), ProguardMemberRule::hasBackReference);
ProguardMemberRule replacementMemberRule =
memberRuleWithBackReferenceIndex >= 0
? representativeKeepRule.getMemberRule(memberRuleWithBackReferenceIndex)
: null;
// Iterate over the other keep rules and merge.
for (ProguardKeepRule keepRule :
Iterables.transform(Iterables.skip(materializedSubsequentRules, 1), Pair::getSecond)) {
assert ObjectUtils.identical(keepRule.getOrigin(), representativeKeepRule.getOrigin());
assert ObjectUtils.identical(keepRule.getPosition(), representativeKeepRule.getPosition());
assert ObjectUtils.identical(keepRule.getSource(), representativeKeepRule.getSource());
assert ObjectUtils.identical(
keepRule.getClassAccessFlags(), representativeKeepRule.getClassAccessFlags());
assert ObjectUtils.identical(
keepRule.getNegatedClassAccessFlags(),
representativeKeepRule.getNegatedClassAccessFlags());
assert ObjectUtils.identical(
keepRule.getClassTypeNegated(), representativeKeepRule.getClassTypeNegated());
assert ObjectUtils.identical(keepRule.getClassType(), representativeKeepRule.getClassType());
assert ObjectUtils.identical(
keepRule.getInheritanceIsExtends(), representativeKeepRule.getInheritanceIsExtends());
assert ObjectUtils.identical(keepRule.getType(), representativeKeepRule.getType());
assert ObjectUtils.identical(keepRule.getModifiers(), representativeKeepRule.getModifiers());
if (keepRule.getClassAnnotations() != classAnnotations
|| !keepRule.getClassNames().equals(classNames)
|| keepRule.getInheritanceAnnotations() != inheritanceAnnotations
|| !Objects.equals(keepRule.getInheritanceClassName(), inheritanceClassName)) {
// Not implemented.
return materializedSubsequentRules;
}
if (replacementMemberRule != null) {
ProguardMemberRule otherMemberRule =
keepRule.getMemberRule(memberRuleWithBackReferenceIndex);
replacementMemberRule =
optimizeMaterializedMemberRulesWithSingleBackReference(
replacementMemberRule, otherMemberRule);
if (replacementMemberRule == null) {
// Not implemented.
return materializedSubsequentRules;
}
}
}
List<ProguardMemberRule> memberRules;
if (replacementMemberRule != null) {
memberRules = new ArrayList<>(representativeKeepRule.getMemberRules());
memberRules.set(memberRuleWithBackReferenceIndex, replacementMemberRule);
} else {
memberRules = representativeKeepRule.getMemberRules();
}
ProguardKeepRule replacementKeepRule =
new ProguardKeepRule(
representativeKeepRule.getOrigin(),
representativeKeepRule.getPosition(),
representativeKeepRule.getSource(),
classAnnotations,
representativeKeepRule.getClassAccessFlags(),
representativeKeepRule.getNegatedClassAccessFlags(),
representativeKeepRule.getClassTypeNegated(),
representativeKeepRule.getClassType(),
classNames,
inheritanceAnnotations,
inheritanceClassName,
representativeKeepRule.getInheritanceIsExtends(),
memberRules,
representativeKeepRule.getType(),
representativeKeepRule.getModifiers());
// TODO(b/368502790): We currently use the reason of the first materialiazed if rule. We should
// either report all reasons, or better, identify which reason lead to the subsequent rule
// matching a given item.
ProguardIfRulePreconditionMatch replacementIfRulePreconditionMatch =
IterableUtils.first(materializedSubsequentRules).getFirst();
return ImmutableList.of(new Pair<>(replacementIfRulePreconditionMatch, replacementKeepRule));
}
private static ProguardMemberRule optimizeMaterializedMemberRulesWithSingleBackReference(
ProguardMemberRule memberRule, ProguardMemberRule other) {
assert ObjectUtils.identical(memberRule.getAccessFlags(), other.getAccessFlags());
assert ObjectUtils.identical(memberRule.getNegatedAccessFlags(), other.getNegatedAccessFlags());
assert ObjectUtils.identical(memberRule.getRuleType(), other.getRuleType());
assert ObjectUtils.identical(memberRule.getReturnValue(), other.getReturnValue());
if (memberRule.getAnnotations() != other.getAnnotations()
|| !memberRule.getName().equals(other.getName())
|| memberRule.getArguments() != other.getArguments()) {
// Not implemented.
return null;
}
ProguardTypeMatcher type = null;
if (memberRule.hasType()) {
type =
optimizeMaterializedTypeMatcherWithSingleBackReference(
memberRule.getType(), other.getType());
if (type == null) {
// Not implemented.
return null;
}
}
return new ProguardMemberRule(
memberRule.getAnnotations(),
memberRule.getAccessFlags(),
memberRule.getNegatedAccessFlags(),
memberRule.getRuleType(),
type,
memberRule.getName(),
memberRule.getArguments(),
memberRule.getReturnValue());
}
private static ProguardTypeMatcher optimizeMaterializedTypeMatcherWithSingleBackReference(
ProguardTypeMatcher type, ProguardTypeMatcher other) {
assert !other.hasSpecificTypes();
if (type.hasSpecificType()) {
if (other.hasSpecificType()) {
return new MatchSpecificTypes(
SetUtils.newIdentityHashSet(type.getSpecificType(), other.getSpecificType()));
}
} else if (type.hasSpecificTypes()) {
if (other.hasSpecificType()) {
type.getSpecificTypes().add(other.getSpecificType());
return type;
}
}
// Not implemented.
return null;
}
}