blob: ba9ec8a35be9e1425abe7488ae57b97111ac939f [file] [log] [blame]
// Copyright (c) 2018, 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 com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class ProguardIfRule extends ProguardKeepRuleBase {
private static final Origin neverInlineOrigin =
new Origin(Origin.root()) {
@Override
public String part() {
return "<SYNTHETIC_NEVER_INLINE_RULE>";
}
};
private final Set<DexReference> preconditions;
final ProguardKeepRule subsequentRule;
public Set<DexReference> getPreconditions() {
return preconditions;
}
public ProguardKeepRule getSubsequentRule() {
return subsequentRule;
}
public static class Builder extends ProguardKeepRuleBase.Builder<ProguardIfRule, Builder> {
ProguardKeepRule subsequentRule = null;
protected Builder() {
super();
}
@Override
public Builder self() {
return this;
}
public void setSubsequentRule(ProguardKeepRule rule) {
subsequentRule = rule;
}
@Override
public ProguardIfRule build() {
assert subsequentRule != null : "Option -if without a subsequent rule.";
return new ProguardIfRule(
origin,
getPosition(),
source,
classAnnotation,
classAccessFlags,
negatedClassAccessFlags,
classTypeNegated,
classType,
classNames,
inheritanceAnnotation,
inheritanceClassName,
inheritanceIsExtends,
memberRules,
subsequentRule,
null);
}
}
private ProguardIfRule(
Origin origin,
Position position,
String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules,
ProguardKeepRule subsequentRule,
Set<DexReference> preconditions) {
super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
inheritanceIsExtends, memberRules,
ProguardKeepRuleType.CONDITIONAL, ProguardKeepRuleModifiers.builder().build());
this.subsequentRule = subsequentRule;
this.preconditions = preconditions;
}
public static Builder builder() {
return new Builder();
}
@Override
protected Iterable<ProguardWildcard> getWildcards() {
return Iterables.concat(super.getWildcards(), subsequentRule.getWildcards());
}
@Override
public boolean isProguardIfRule() {
return true;
}
@Override
public ProguardIfRule asProguardIfRule() {
return this;
}
protected ProguardIfRule materialize(
DexItemFactory dexItemFactory, Set<DexReference> preconditions) {
return new ProguardIfRule(
getOrigin(),
getPosition(),
getSource(),
getClassAnnotation() == null ? null : getClassAnnotation().materialize(dexItemFactory),
getClassAccessFlags(),
getNegatedClassAccessFlags(),
getClassTypeNegated(),
getClassType(),
getClassNames().materialize(dexItemFactory),
getInheritanceAnnotation() == null
? null
: getInheritanceAnnotation().materialize(dexItemFactory),
getInheritanceClassName() == null
? null
: getInheritanceClassName().materialize(dexItemFactory),
getInheritanceIsExtends(),
getMemberRules() == null
? null
: getMemberRules().stream()
.map(memberRule -> memberRule.materialize(dexItemFactory))
.collect(Collectors.toList()),
subsequentRule.materialize(dexItemFactory),
preconditions);
}
protected ClassInlineRule neverClassInlineRuleForCondition(DexItemFactory dexItemFactory) {
return new ClassInlineRule(
neverInlineOrigin,
Position.UNKNOWN,
null,
getClassAnnotation() == null ? null : getClassAnnotation().materialize(dexItemFactory),
getClassAccessFlags(),
getNegatedClassAccessFlags(),
getClassTypeNegated(),
getClassType(),
getClassNames().materialize(dexItemFactory),
getInheritanceAnnotation() == null
? null
: getInheritanceAnnotation().materialize(dexItemFactory),
getInheritanceClassName() == null
? null
: getInheritanceClassName().materialize(dexItemFactory),
getInheritanceIsExtends(),
ImmutableList.of(),
ClassInlineRule.Type.NEVER);
}
/**
* Consider the following rule, which requests that class Y should be kept if the method X.m() is
* in the final output.
*
* <pre>
* -if class X {
* public void m();
* }
* -keep class Y
* </pre>
*
* When the {@link Enqueuer} finds that the method X.m() is reachable, it applies the subsequent
* keep rule of the -if rule. Thus, Y will be marked as pinned, which guarantees, for example,
* that it will not be merged into another class by the vertical class merger.
*
* <p>However, when the {@link Enqueuer} runs for the second time, it is important that X.m() has
* not been inlined into another method Z.z(), because that would mean that Z.z() now relies on
* the presence of Y, meanwhile Y will not be kept because X.m() is no longer present.
*
* <p>Therefore, each time the subsequent rule of an -if rule is applied, we also apply a
* -neverinline rule for the condition of the -if rule.
*/
protected InlineRule neverInlineRuleForCondition(DexItemFactory dexItemFactory) {
if (getMemberRules() == null || getMemberRules().isEmpty()) {
return null;
}
return new InlineRule(
neverInlineOrigin,
Position.UNKNOWN,
null,
getClassAnnotation() == null ? null : getClassAnnotation().materialize(dexItemFactory),
getClassAccessFlags(),
getNegatedClassAccessFlags(),
getClassTypeNegated(),
getClassType(),
getClassNames().materialize(dexItemFactory),
getInheritanceAnnotation() == null
? null
: getInheritanceAnnotation().materialize(dexItemFactory),
getInheritanceClassName() == null
? null
: getInheritanceClassName().materialize(dexItemFactory),
getInheritanceIsExtends(),
getMemberRules().stream()
.filter(rule -> rule.getRuleType().includesMethods())
.map(memberRule -> memberRule.materialize(dexItemFactory))
.collect(Collectors.toList()),
InlineRule.Type.NEVER);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ProguardIfRule)) {
return false;
}
ProguardIfRule other = (ProguardIfRule) o;
if (!subsequentRule.equals(other.subsequentRule)) {
return false;
}
return super.equals(o);
}
@Override
public int hashCode() {
return super.hashCode() * 3 + subsequentRule.hashCode();
}
@Override
String typeString() {
return "if";
}
}