Version 2.1.38 Cherry pick: Support for multiple class, inheritance and member annotations CL: https://r8-review.googlesource.com/c/r8/+/52010 Cherry pick: Remove ImmutableList.builderWithExpectedSize() use CL: https://r8-review.googlesource.com/c/r8/+/52040 Bug: 158454684 Change-Id: I961eb124a597b733fe94cacb877bbb2b84725569
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java index f4cd7bf..bb3e17f 100644 --- a/src/main/java/com/android/tools/r8/Version.java +++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@ // This field is accessed from release scripts using simple pattern matching. // Therefore, changing this field could break our release scripts. - public static final String LABEL = "2.1.37"; + public static final String LABEL = "2.1.38"; private Version() { }
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationMatchResult.java b/src/main/java/com/android/tools/r8/shaking/AnnotationMatchResult.java index f5a3bbf..5b97077 100644 --- a/src/main/java/com/android/tools/r8/shaking/AnnotationMatchResult.java +++ b/src/main/java/com/android/tools/r8/shaking/AnnotationMatchResult.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.shaking; import com.android.tools.r8.graph.DexAnnotation; +import java.util.List; public abstract class AnnotationMatchResult { @@ -30,14 +31,14 @@ static class ConcreteAnnotationMatchResult extends AnnotationMatchResult { - private final DexAnnotation matchedAnnotation; + private final List<DexAnnotation> matchedAnnotations; - public ConcreteAnnotationMatchResult(DexAnnotation matchedAnnotation) { - this.matchedAnnotation = matchedAnnotation; + public ConcreteAnnotationMatchResult(List<DexAnnotation> matchedAnnotations) { + this.matchedAnnotations = matchedAnnotations; } - public DexAnnotation getMatchedAnnotation() { - return matchedAnnotation; + public List<DexAnnotation> getMatchedAnnotations() { + return matchedAnnotations; } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInlineRule.java b/src/main/java/com/android/tools/r8/shaking/ClassInlineRule.java index 4e69cd2..ccda8e3 100644 --- a/src/main/java/com/android/tools/r8/shaking/ClassInlineRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ClassInlineRule.java
@@ -39,13 +39,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules, @@ -59,13 +59,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, @@ -74,13 +74,13 @@ origin, position, source, - classAnnotation, + classAnnotations, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + inheritanceAnnotations, inheritanceClassName, inheritanceIsExtends, memberRules);
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassMergingRule.java b/src/main/java/com/android/tools/r8/shaking/ClassMergingRule.java index 43bfe4b..ee57043 100644 --- a/src/main/java/com/android/tools/r8/shaking/ClassMergingRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ClassMergingRule.java
@@ -34,9 +34,21 @@ @Override public ClassMergingRule build() { - return new ClassMergingRule(origin, getPosition(), source, classAnnotation, classAccessFlags, - negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation, - inheritanceClassName, inheritanceIsExtends, memberRules, type); + return new ClassMergingRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules, + type); } } @@ -46,20 +58,31 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, Type type) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); this.type = type; }
diff --git a/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java index 8288bde..540db63 100644 --- a/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java +++ b/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java
@@ -24,7 +24,7 @@ if (enqueuer.getMode().isInitialTreeShaking() && annotationMatchResult.isConcreteAnnotationMatchResult()) { enqueuer.retainAnnotationForFinalTreeShaking( - annotationMatchResult.asConcreteAnnotationMatchResult().getMatchedAnnotation()); + annotationMatchResult.asConcreteAnnotationMatchResult().getMatchedAnnotations()); } } }
diff --git a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java index 6d6f424..bc33778 100644 --- a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
@@ -27,13 +27,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules); @@ -44,13 +44,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { @@ -58,13 +58,13 @@ origin, position, source, - classAnnotation, + classAnnotations, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + inheritanceAnnotations, inheritanceClassName, inheritanceIsExtends, memberRules);
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 1cc091d..6878c05 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3334,10 +3334,10 @@ desugaredLambdaImplementationMethods.clear(); } - void retainAnnotationForFinalTreeShaking(DexAnnotation annotation) { + void retainAnnotationForFinalTreeShaking(List<DexAnnotation> annotations) { assert mode.isInitialTreeShaking(); if (annotationRemoverBuilder != null) { - annotationRemoverBuilder.retainAnnotation(annotation); + annotations.forEach(annotationRemoverBuilder::retainAnnotation); } }
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleClassPartEquivalence.java b/src/main/java/com/android/tools/r8/shaking/IfRuleClassPartEquivalence.java index aa37771..527565f 100644 --- a/src/main/java/com/android/tools/r8/shaking/IfRuleClassPartEquivalence.java +++ b/src/main/java/com/android/tools/r8/shaking/IfRuleClassPartEquivalence.java
@@ -12,7 +12,7 @@ @Override protected boolean doEquivalent(ProguardIfRule p1, ProguardIfRule p2) { - if (!Objects.equals(p1.getClassAnnotation(), p2.getClassAnnotation())) { + if (!p1.getClassAnnotations().equals(p2.getClassAnnotations())) { return false; } if (!p1.getClassAccessFlags().equals(p2.getClassAccessFlags()) @@ -26,7 +26,7 @@ if (p1.getInheritanceIsExtends() != p2.getInheritanceIsExtends()) { return false; } - if (!Objects.equals(p1.getInheritanceAnnotation(), p2.getInheritanceAnnotation())) { + if (!p1.getInheritanceAnnotations().equals(p2.getInheritanceAnnotations())) { return false; } if (!Objects.equals(p1.getInheritanceClassName(), p2.getInheritanceClassName())) { @@ -40,17 +40,13 @@ @Override protected int doHash(ProguardIfRule rule) { - int result = (rule.getClassAnnotation() != null ? rule.getClassAnnotation().hashCode() : 0); + int result = rule.getClassAnnotations().hashCode(); result = 3 * result + rule.getClassAccessFlags().hashCode(); result = 3 * result + rule.getNegatedClassAccessFlags().hashCode(); result = 3 * result + (rule.getClassTypeNegated() ? 1 : 0); result = 3 * result + (rule.getClassType() != null ? rule.getClassType().hashCode() : 0); result = 3 * result + rule.getClassNames().hashCode(); - result = - 3 * result - + (rule.getInheritanceAnnotation() != null - ? rule.getInheritanceAnnotation().hashCode() - : 0); + result = 3 * result + rule.getInheritanceAnnotations().hashCode(); result = 3 * result + (rule.getInheritanceClassName() != null
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java index ca746a1..41b6b33 100644 --- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java +++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -41,9 +41,21 @@ @Override public InlineRule build() { - return new InlineRule(origin, getPosition(), source, classAnnotation, classAccessFlags, - negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation, - inheritanceClassName, inheritanceIsExtends, memberRules, type); + return new InlineRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules, + type); } } @@ -53,20 +65,31 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, Type type) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); this.type = type; } @@ -83,13 +106,13 @@ ProguardCheckDiscardRule.Builder builder = ProguardCheckDiscardRule.builder(); builder.setOrigin(checkDiscardOrigin); builder.setSource(null); - builder.setClassAnnotation(getClassAnnotation()); + builder.addClassAnnotations(getClassAnnotations()); builder.setClassAccessFlags(getClassAccessFlags()); builder.setNegatedClassAccessFlags(getNegatedClassAccessFlags()); builder.setClassTypeNegated(getClassTypeNegated()); builder.setClassType(getClassType()); builder.setClassNames(getClassNames()); - builder.setInheritanceAnnotation(getInheritanceAnnotation()); + builder.addInheritanceAnnotations(getInheritanceAnnotations()); builder.setInheritanceIsExtends(getInheritanceIsExtends()); builder.setMemberRules(getMemberRules()); return builder.build();
diff --git a/src/main/java/com/android/tools/r8/shaking/MemberValuePropagationRule.java b/src/main/java/com/android/tools/r8/shaking/MemberValuePropagationRule.java index dde4631..c26832b 100644 --- a/src/main/java/com/android/tools/r8/shaking/MemberValuePropagationRule.java +++ b/src/main/java/com/android/tools/r8/shaking/MemberValuePropagationRule.java
@@ -35,9 +35,21 @@ @Override public MemberValuePropagationRule build() { - return new MemberValuePropagationRule(origin, getPosition(), source, classAnnotation, - classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules, type); + return new MemberValuePropagationRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules, + type); } } @@ -47,20 +59,31 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, Type type) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); this.type = type; }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java index 01b7a5e..654bc3e 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java
@@ -27,13 +27,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules); @@ -44,13 +44,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { @@ -58,13 +58,13 @@ origin, position, source, - classAnnotation, + classAnnotations, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + inheritanceAnnotations, inheritanceClassName, inheritanceIsExtends, memberRules);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java index 5f8e860..ed151aa 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -23,9 +23,20 @@ @Override public ProguardAssumeNoSideEffectRule build() { - return new ProguardAssumeNoSideEffectRule(origin, getPosition(), source, classAnnotation, - classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules); + return new ProguardAssumeNoSideEffectRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules); } } @@ -33,19 +44,30 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); } /**
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java index 8b8f4dd..97d901e 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -23,9 +23,20 @@ @Override public ProguardAssumeValuesRule build() { - return new ProguardAssumeValuesRule(origin, getPosition(), source, classAnnotation, - classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules); + return new ProguardAssumeValuesRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules); } } @@ -33,19 +44,30 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); } /**
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java index 664cb25..82b81a4 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -23,9 +23,20 @@ @Override public ProguardCheckDiscardRule build() { - return new ProguardCheckDiscardRule(origin, getPosition(), source, classAnnotation, - classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules); + return new ProguardCheckDiscardRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules); } } @@ -33,19 +44,30 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); } public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java index 9ae4986..2bf270a 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.position.TextRange; import com.android.tools.r8.utils.StringUtils; import com.google.common.collect.ImmutableList; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -22,13 +23,15 @@ protected Position start; protected Position end; protected String source; - protected ProguardTypeMatcher classAnnotation; + private final ImmutableList.Builder<ProguardTypeMatcher> classAnnotations = + ImmutableList.builder(); protected ProguardAccessFlags classAccessFlags = new ProguardAccessFlags(); protected ProguardAccessFlags negatedClassAccessFlags = new ProguardAccessFlags(); protected boolean classTypeNegated = false; protected ProguardClassType classType = ProguardClassType.UNSPECIFIED; protected ProguardClassNameList classNames; - protected ProguardTypeMatcher inheritanceAnnotation; + private final ImmutableList.Builder<ProguardTypeMatcher> inheritanceAnnotations = + ImmutableList.builder(); protected ProguardTypeMatcher inheritanceClassName; protected boolean inheritanceIsExtends = false; protected List<ProguardMemberRule> memberRules = new LinkedList<>(); @@ -105,12 +108,13 @@ this.inheritanceClassName = inheritanceClassName; } - public ProguardTypeMatcher getInheritanceAnnotation() { - return inheritanceAnnotation; + public void addInheritanceAnnotations(List<ProguardTypeMatcher> inheritanceAnnotations) { + assert inheritanceAnnotations != null; + this.inheritanceAnnotations.addAll(inheritanceAnnotations); } - public void setInheritanceAnnotation(ProguardTypeMatcher inheritanceAnnotation) { - this.inheritanceAnnotation = inheritanceAnnotation; + public List<ProguardTypeMatcher> buildInheritanceAnnotations() { + return inheritanceAnnotations.build(); } public ProguardClassNameList getClassNames() { @@ -155,12 +159,17 @@ negatedClassAccessFlags = flags; } - public ProguardTypeMatcher getClassAnnotation() { - return classAnnotation; + public void addClassAnnotation(ProguardTypeMatcher classAnnotation) { + classAnnotations.add(classAnnotation); } - public void setClassAnnotation(ProguardTypeMatcher classAnnotation) { - this.classAnnotation = classAnnotation; + public void addClassAnnotations(List<ProguardTypeMatcher> classAnnotations) { + assert classAnnotations != null; + this.classAnnotations.addAll(classAnnotations); + } + + public List<ProguardTypeMatcher> buildClassAnnotations() { + return classAnnotations.build(); } protected void matchAllSpecification() { @@ -172,13 +181,13 @@ private final Origin origin; private final Position position; private final String source; - private final ProguardTypeMatcher classAnnotation; + private final List<ProguardTypeMatcher> classAnnotations; private final ProguardAccessFlags classAccessFlags; private final ProguardAccessFlags negatedClassAccessFlags; private final boolean classTypeNegated; private final ProguardClassType classType; private final ProguardClassNameList classNames; - private final ProguardTypeMatcher inheritanceAnnotation; + private final List<ProguardTypeMatcher> inheritanceAnnotations; private final ProguardTypeMatcher inheritanceClassName; private final boolean inheritanceIsExtends; private final List<ProguardMemberRule> memberRules; @@ -187,13 +196,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { @@ -202,15 +211,15 @@ assert source != null || origin != Origin.unknown(); this.origin = origin; this.position = position; - this.source =source; - this.classAnnotation = classAnnotation; + this.source = source; + this.classAnnotations = classAnnotations; this.classAccessFlags = classAccessFlags; this.negatedClassAccessFlags = negatedClassAccessFlags; this.classTypeNegated = classTypeNegated; this.classType = classType; assert classType != null; this.classNames = classNames; - this.inheritanceAnnotation = inheritanceAnnotation; + this.inheritanceAnnotations = inheritanceAnnotations; this.inheritanceClassName = inheritanceClassName; this.inheritanceIsExtends = inheritanceIsExtends; this.memberRules = memberRules; @@ -248,8 +257,8 @@ return inheritanceClassName; } - public ProguardTypeMatcher getInheritanceAnnotation() { - return inheritanceAnnotation; + public List<ProguardTypeMatcher> getInheritanceAnnotations() { + return inheritanceAnnotations; } public ProguardClassNameList getClassNames() { @@ -272,8 +281,8 @@ return negatedClassAccessFlags; } - public ProguardTypeMatcher getClassAnnotation() { - return classAnnotation; + public List<ProguardTypeMatcher> getClassAnnotations() { + return classAnnotations; } @Override @@ -289,7 +298,7 @@ if (inheritanceIsExtends != that.inheritanceIsExtends) { return false; } - if (!Objects.equals(classAnnotation, that.classAnnotation)) { + if (!Objects.equals(classAnnotations, that.classAnnotations)) { return false; } if (!classAccessFlags.equals(that.classAccessFlags)) { @@ -304,7 +313,7 @@ if (!classNames.equals(that.classNames)) { return false; } - if (!Objects.equals(inheritanceAnnotation, that.inheritanceAnnotation)) { + if (!Objects.equals(inheritanceAnnotations, that.inheritanceAnnotations)) { return false; } if (!Objects.equals(inheritanceClassName, that.inheritanceClassName)) { @@ -316,13 +325,13 @@ @Override public int hashCode() { // Used multiplier 3 to avoid too much overflow when computing hashCode. - int result = (classAnnotation != null ? classAnnotation.hashCode() : 0); + int result = classAnnotations.hashCode(); result = 3 * result + classAccessFlags.hashCode(); result = 3 * result + negatedClassAccessFlags.hashCode(); result = 3 * result + (classTypeNegated ? 1 : 0); result = 3 * result + (classType != null ? classType.hashCode() : 0); result = 3 * result + classNames.hashCode(); - result = 3 * result + (inheritanceAnnotation != null ? inheritanceAnnotation.hashCode() : 0); + result = 3 * result + inheritanceAnnotations.hashCode(); result = 3 * result + (inheritanceClassName != null ? inheritanceClassName.hashCode() : 0); result = 3 * result + (inheritanceIsExtends ? 1 : 0); result = 3 * result + memberRules.hashCode(); @@ -330,9 +339,9 @@ } protected StringBuilder append(StringBuilder builder, boolean includeMemberRules) { + appendAnnotations(classAnnotations, builder); boolean needsSpaceBeforeClassType = - StringUtils.appendNonEmpty(builder, "@", classAnnotation, null) - | StringUtils.appendNonEmpty(builder, "", classAccessFlags, null) + StringUtils.appendNonEmpty(builder, null, classAccessFlags, null) | StringUtils.appendNonEmpty( builder, "!", negatedClassAccessFlags.toString().replace(" ", " !"), null); if (needsSpaceBeforeClassType) { @@ -345,9 +354,8 @@ builder.append(' '); classNames.writeTo(builder); if (hasInheritanceClassName()) { - builder.append(' ').append(inheritanceIsExtends ? "extends" : "implements"); - StringUtils.appendNonEmpty(builder, "@", inheritanceAnnotation, null); - builder.append(' '); + builder.append(' ').append(inheritanceIsExtends ? "extends" : "implements").append(' '); + appendAnnotations(inheritanceAnnotations, builder); builder.append(inheritanceClassName); } if (includeMemberRules && !memberRules.isEmpty()) { @@ -362,6 +370,18 @@ return builder; } + private static void appendAnnotations( + List<ProguardTypeMatcher> annotations, StringBuilder builder) { + if (!annotations.isEmpty()) { + Iterator<ProguardTypeMatcher> annotationIterator = annotations.iterator(); + builder.append('@').append(annotationIterator.next()); + while (annotationIterator.hasNext()) { + builder.append(" @").append(annotationIterator.next()); + } + builder.append(' '); + } + } + /** * Short String representation without member rules. */
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java index aee4a83..58bbdae 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -897,7 +897,7 @@ ProguardClassSpecification.Builder<C, B> builder, boolean allowValueSpecification) throws ProguardRuleParserException { - parseClassFlagsAndAnnotations(builder); + parseClassAnnotationsAndFlags(builder); parseClassType(builder); builder.setClassNames(parseClassNames()); parseInheritance(builder); @@ -965,6 +965,18 @@ } } + private List<ProguardTypeMatcher> parseAnnotationList() throws ProguardRuleParserException { + List<ProguardTypeMatcher> annotations = null; + ProguardTypeMatcher current; + while ((current = parseAnnotation()) != null) { + if (annotations == null) { + annotations = new ArrayList<>(2); + } + annotations.add(current); + } + return annotations != null ? annotations : Collections.emptyList(); + } + private ProguardTypeMatcher parseAnnotation() throws ProguardRuleParserException { skipWhitespace(); int startPosition = position; @@ -988,16 +1000,14 @@ return acceptChar('!'); } - private void parseClassFlagsAndAnnotations(ProguardClassSpecification.Builder builder) + private void parseClassAnnotationsAndFlags(ProguardClassSpecification.Builder<?, ?> builder) throws ProguardRuleParserException { + // We allow interleaving the class annotations and class flags for compatibility with + // Proguard, although this should not be possible according to the grammar. while (true) { - skipWhitespace(); ProguardTypeMatcher annotation = parseAnnotation(); if (annotation != null) { - // TODO(ager): Should we only allow one annotation? It looks that way from the - // proguard keep rule description, but that seems like a strange restriction? - assert builder.getClassAnnotation() == null; - builder.setClassAnnotation(annotation); + builder.addClassAnnotation(annotation); } else { int start = position; ProguardAccessFlags flags = @@ -1025,7 +1035,7 @@ "Expected [!]interface|@interface|class|enum", origin, getPosition(start)); } - private void parseClassType(ProguardClassSpecification.Builder builder) { + private void parseClassType(ProguardClassSpecification.Builder<?, ?> builder) { skipWhitespace(); TextPosition start = getPosition(); if (acceptChar('!')) { @@ -1049,7 +1059,8 @@ } } - private void parseInheritance(ProguardClassSpecification.Builder classSpecificationBuilder) + private void parseInheritance( + ProguardClassSpecification.Builder<?, ?> classSpecificationBuilder) throws ProguardRuleParserException { skipWhitespace(); if (acceptString("implements")) { @@ -1059,7 +1070,7 @@ } else { return; } - classSpecificationBuilder.setInheritanceAnnotation(parseAnnotation()); + classSpecificationBuilder.addInheritanceAnnotations(parseAnnotationList()); classSpecificationBuilder.setInheritanceClassName(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory)); } @@ -1084,8 +1095,7 @@ private ProguardMemberRule parseMemberRule(boolean allowValueSpecification) throws ProguardRuleParserException { ProguardMemberRule.Builder ruleBuilder = ProguardMemberRule.builder(); - skipWhitespace(); - ruleBuilder.setAnnotation(parseAnnotation()); + ruleBuilder.setAnnotations(parseAnnotationList()); parseMemberAccessFlags(ruleBuilder); parseMemberPattern(ruleBuilder, allowValueSpecification); return ruleBuilder.isValid() ? ruleBuilder.build() : null;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java index 0e21e66..4a19c6f 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -27,19 +27,30 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); } public boolean isUsed() { @@ -124,17 +135,16 @@ protected Iterable<ProguardWildcard> getWildcards() { List<ProguardMemberRule> memberRules = getMemberRules(); return Iterables.concat( - ProguardTypeMatcher.getWildcardsOrEmpty(getClassAnnotation()), + ProguardTypeMatcher.getWildcardsOrEmpty(getClassAnnotations()), ProguardClassNameList.getWildcardsOrEmpty(getClassNames()), - ProguardTypeMatcher.getWildcardsOrEmpty(getInheritanceAnnotation()), + ProguardTypeMatcher.getWildcardsOrEmpty(getInheritanceAnnotations()), ProguardTypeMatcher.getWildcardsOrEmpty(getInheritanceClassName()), memberRules != null ? memberRules.stream() - .map(ProguardMemberRule::getWildcards) - .flatMap(it -> StreamSupport.stream(it.spliterator(), false)) + .map(ProguardMemberRule::getWildcards) + .flatMap(it -> StreamSupport.stream(it.spliterator(), false)) ::iterator - : Collections::emptyIterator - ); + : Collections::emptyIterator); } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java index 8ff7670..37c75a1 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -22,9 +22,20 @@ @Override public ProguardIdentifierNameStringRule build() { - return new ProguardIdentifierNameStringRule(origin, getPosition(), source, classAnnotation, - classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules); + return new ProguardIdentifierNameStringRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules); } } @@ -32,19 +43,30 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); } public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java index ba9ec8a..c8e185f 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -58,13 +58,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules, @@ -77,22 +77,34 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, 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()); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules, + ProguardKeepRuleType.CONDITIONAL, + ProguardKeepRuleModifiers.builder().build()); this.subsequentRule = subsequentRule; this.preconditions = preconditions; } @@ -122,15 +134,13 @@ getOrigin(), getPosition(), getSource(), - getClassAnnotation() == null ? null : getClassAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getClassAnnotations(), dexItemFactory), getClassAccessFlags(), getNegatedClassAccessFlags(), getClassTypeNegated(), getClassType(), getClassNames().materialize(dexItemFactory), - getInheritanceAnnotation() == null - ? null - : getInheritanceAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getInheritanceAnnotations(), dexItemFactory), getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(dexItemFactory), @@ -149,15 +159,13 @@ neverInlineOrigin, Position.UNKNOWN, null, - getClassAnnotation() == null ? null : getClassAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getClassAnnotations(), dexItemFactory), getClassAccessFlags(), getNegatedClassAccessFlags(), getClassTypeNegated(), getClassType(), getClassNames().materialize(dexItemFactory), - getInheritanceAnnotation() == null - ? null - : getInheritanceAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getInheritanceAnnotations(), dexItemFactory), getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(dexItemFactory), @@ -196,15 +204,13 @@ neverInlineOrigin, Position.UNKNOWN, null, - getClassAnnotation() == null ? null : getClassAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getClassAnnotations(), dexItemFactory), getClassAccessFlags(), getNegatedClassAccessFlags(), getClassTypeNegated(), getClassType(), getClassNames().materialize(dexItemFactory), - getInheritanceAnnotation() == null - ? null - : getInheritanceAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getInheritanceAnnotations(), dexItemFactory), getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(dexItemFactory),
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java index 4633882..305c108 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -25,9 +25,22 @@ @Override public ProguardKeepRule build() { - return new ProguardKeepRule(origin, getPosition(), source, classAnnotation, classAccessFlags, - negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation, - inheritanceClassName, inheritanceIsExtends, memberRules, type, modifiersBuilder.build()); + return new ProguardKeepRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules, + type, + modifiersBuilder.build()); } } @@ -35,21 +48,34 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, ProguardKeepRuleType type, ProguardKeepRuleModifiers modifiers) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules, type, modifiers); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules, + type, + modifiers); } /** @@ -64,15 +90,13 @@ getOrigin(), getPosition(), getSource(), - getClassAnnotation() == null ? null : getClassAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getClassAnnotations(), dexItemFactory), getClassAccessFlags(), getNegatedClassAccessFlags(), getClassTypeNegated(), getClassType(), getClassNames() == null ? null : getClassNames().materialize(dexItemFactory), - getInheritanceAnnotation() == null - ? null - : getInheritanceAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getInheritanceAnnotations(), dexItemFactory), getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(dexItemFactory),
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java index 7455469..b87e5a6 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
@@ -43,21 +43,32 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, ProguardKeepRuleType type, ProguardKeepRuleModifiers modifiers) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); this.type = type; this.modifiers = modifiers; }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java index 1a894d3..3034f6d 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -24,7 +24,7 @@ public static class Builder { - private ProguardTypeMatcher annotation; + private List<ProguardTypeMatcher> annotations = Collections.emptyList(); private ProguardAccessFlags accessFlags = new ProguardAccessFlags(); private ProguardAccessFlags negatedAccessFlags = new ProguardAccessFlags(); private ProguardMemberType ruleType; @@ -35,8 +35,9 @@ private Builder() {} - public void setAnnotation(ProguardTypeMatcher annotation) { - this.annotation = annotation; + public void setAnnotations(List<ProguardTypeMatcher> annotations) { + assert annotations != null; + this.annotations = annotations; } public ProguardAccessFlags getAccessFlags() { @@ -91,12 +92,19 @@ public ProguardMemberRule build() { assert isValid(); - return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, type, - name, arguments, returnValue); + return new ProguardMemberRule( + annotations, + accessFlags, + negatedAccessFlags, + ruleType, + type, + name, + arguments, + returnValue); } } - private final ProguardTypeMatcher annotation; + private final List<ProguardTypeMatcher> annotations; private final ProguardAccessFlags accessFlags; private final ProguardAccessFlags negatedAccessFlags; private final ProguardMemberType ruleType; @@ -106,7 +114,7 @@ private final ProguardMemberRuleReturnValue returnValue; private ProguardMemberRule( - ProguardTypeMatcher annotation, + List<ProguardTypeMatcher> annotations, ProguardAccessFlags accessFlags, ProguardAccessFlags negatedAccessFlags, ProguardMemberType ruleType, @@ -114,7 +122,7 @@ ProguardNameMatcher name, List<ProguardTypeMatcher> arguments, ProguardMemberRuleReturnValue returnValue) { - this.annotation = annotation; + this.annotations = annotations; this.accessFlags = accessFlags; this.negatedAccessFlags = negatedAccessFlags; this.ruleType = ruleType; @@ -131,8 +139,8 @@ return new Builder(); } - public ProguardTypeMatcher getAnnotation() { - return annotation; + public List<ProguardTypeMatcher> getAnnotations() { + return annotations; } public ProguardAccessFlags getAccessFlags() { @@ -187,7 +195,8 @@ break; } // Annotations check. - return RootSetBuilder.containsAnnotation(annotation, field, matchedAnnotationsConsumer); + return RootSetBuilder.containsAllAnnotations( + annotations, field, matchedAnnotationsConsumer); } case FIELD: @@ -207,7 +216,8 @@ break; } // Annotations check - return RootSetBuilder.containsAnnotation(annotation, field, matchedAnnotationsConsumer); + return RootSetBuilder.containsAllAnnotations( + annotations, field, matchedAnnotationsConsumer); } case ALL_METHODS: @@ -241,7 +251,8 @@ break; } // Annotations check. - return RootSetBuilder.containsAnnotation(annotation, method, matchedAnnotationsConsumer); + return RootSetBuilder.containsAllAnnotations( + annotations, method, matchedAnnotationsConsumer); } case METHOD: @@ -266,7 +277,8 @@ break; } // Annotations check. - if (!RootSetBuilder.containsAnnotation(annotation, method, matchedAnnotationsConsumer)) { + if (!RootSetBuilder.containsAllAnnotations( + annotations, method, matchedAnnotationsConsumer)) { return false; } // Parameter types check. @@ -309,21 +321,20 @@ Iterable<ProguardWildcard> getWildcards() { return Iterables.concat( - ProguardTypeMatcher.getWildcardsOrEmpty(annotation), + ProguardTypeMatcher.getWildcardsOrEmpty(annotations), ProguardTypeMatcher.getWildcardsOrEmpty(type), ProguardNameMatcher.getWildcardsOrEmpty(name), arguments != null ? arguments.stream() - .map(ProguardTypeMatcher::getWildcards) - .flatMap(it -> StreamSupport.stream(it.spliterator(), false)) + .map(ProguardTypeMatcher::getWildcards) + .flatMap(it -> StreamSupport.stream(it.spliterator(), false)) ::iterator - : Collections::emptyIterator - ); + : Collections::emptyIterator); } ProguardMemberRule materialize(DexItemFactory dexItemFactory) { return new ProguardMemberRule( - getAnnotation() == null ? null : getAnnotation().materialize(dexItemFactory), + ProguardTypeMatcher.materializeList(getAnnotations(), dexItemFactory), getAccessFlags(), getNegatedAccessFlags(), getRuleType(), @@ -345,7 +356,7 @@ ProguardMemberRule that = (ProguardMemberRule) o; - if (annotation != null ? !annotation.equals(that.annotation) : that.annotation != null) { + if (!annotations.equals(that.annotations)) { return false; } if (!accessFlags.equals(that.accessFlags)) { @@ -368,7 +379,7 @@ @Override public int hashCode() { - int result = annotation != null ? annotation.hashCode() : 0; + int result = annotations.hashCode(); result = 31 * result + accessFlags.hashCode(); result = 31 * result + negatedAccessFlags.hashCode(); result = 31 * result + (ruleType != null ? ruleType.hashCode() : 0); @@ -381,7 +392,9 @@ @Override public String toString() { StringBuilder result = new StringBuilder(); - ProguardKeepRule.appendNonEmpty(result, "@", annotation, " "); + for (ProguardTypeMatcher annotation : annotations) { + ProguardKeepRule.appendNonEmpty(result, "@", annotation, " "); + } ProguardKeepRule.appendNonEmpty(result, null, accessFlags, " "); ProguardKeepRule .appendNonEmpty(result, null, negatedAccessFlags.toString().replace(" ", " !"), " ");
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java index a4db6a2..19bd876 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -36,6 +36,10 @@ TYPE } + public MatchSpecificType asSpecificTypeMatcher() { + return null; + } + // Evaluates this matcher on the given type. public abstract boolean matches(DexType type); @@ -59,10 +63,30 @@ return typeMatcher == null ? Collections::emptyIterator : typeMatcher.getWildcards(); } + static Iterable<ProguardWildcard> getWildcardsOrEmpty(List<ProguardTypeMatcher> typeMatchers) { + List<ProguardWildcard> result = new ArrayList<>(); + for (ProguardTypeMatcher typeMatcher : typeMatchers) { + typeMatcher.getWildcards().forEach(result::add); + } + return result; + } + protected ProguardTypeMatcher materialize(DexItemFactory dexItemFactory) { return this; } + public static List<ProguardTypeMatcher> materializeList( + List<ProguardTypeMatcher> matchers, DexItemFactory dexItemFactory) { + if (matchers.isEmpty()) { + return Collections.emptyList(); + } + ImmutableList.Builder<ProguardTypeMatcher> builder = ImmutableList.builder(); + for (ProguardTypeMatcher matcher : matchers) { + builder.add(matcher.materialize(dexItemFactory)); + } + return builder.build(); + } + @Override public abstract String toString(); @@ -312,6 +336,11 @@ } @Override + public MatchSpecificType asSpecificTypeMatcher() { + return this; + } + + @Override public boolean matches(DexType type) { return this.type == type; }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java index 87200fb..4083ff5 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -23,9 +23,20 @@ @Override public ProguardWhyAreYouKeepingRule build() { - return new ProguardWhyAreYouKeepingRule(origin, getPosition(), source, classAnnotation, - classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules); + return new ProguardWhyAreYouKeepingRule( + origin, + getPosition(), + source, + buildClassAnnotations(), + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + buildInheritanceAnnotations(), + inheritanceClassName, + inheritanceIsExtends, + memberRules); } } @@ -33,19 +44,30 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { - super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags, - classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName, - inheritanceIsExtends, memberRules); + super( + origin, + position, + source, + classAnnotations, + classAccessFlags, + negatedClassAccessFlags, + classTypeNegated, + classType, + classNames, + inheritanceAnnotations, + inheritanceClassName, + inheritanceIsExtends, + memberRules); } public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java index 58e03d9..cfcd1fc 100644 --- a/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java
@@ -40,13 +40,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules, @@ -60,13 +60,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, @@ -75,13 +75,13 @@ origin, position, source, - classAnnotation, + classAnnotations, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + inheritanceAnnotations, inheritanceClassName, inheritanceIsExtends, memberRules);
diff --git a/src/main/java/com/android/tools/r8/shaking/ReprocessMethodRule.java b/src/main/java/com/android/tools/r8/shaking/ReprocessMethodRule.java index 8e8e288..2c7d19a 100644 --- a/src/main/java/com/android/tools/r8/shaking/ReprocessMethodRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ReprocessMethodRule.java
@@ -40,13 +40,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules, @@ -60,13 +60,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules, @@ -75,13 +75,13 @@ origin, position, source, - classAnnotation, + classAnnotations, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + inheritanceAnnotations, inheritanceClassName, inheritanceIsExtends, memberRules);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java index 73171b4..8419104 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -754,7 +754,7 @@ } static AnnotationMatchResult satisfyAnnotation(ProguardConfigurationRule rule, DexClass clazz) { - return containsAnnotation(rule.getClassAnnotation(), clazz); + return containsAllAnnotations(rule.getClassAnnotations(), clazz); } boolean satisfyInheritanceRule(DexClass clazz, ProguardConfigurationRule rule) { @@ -786,7 +786,7 @@ // annotations of `class`. if (rule.getInheritanceClassName().matches(clazz.type, appView)) { AnnotationMatchResult annotationMatchResult = - containsAnnotation(rule.getInheritanceAnnotation(), clazz); + containsAllAnnotations(rule.getInheritanceAnnotations(), clazz); if (annotationMatchResult != null) { handleMatchedAnnotation(annotationMatchResult); return true; @@ -823,7 +823,7 @@ // annotations of `ifaceClass`. if (rule.getInheritanceClassName().matches(iface, appView)) { AnnotationMatchResult annotationMatchResult = - containsAnnotation(rule.getInheritanceAnnotation(), ifaceClass); + containsAllAnnotations(rule.getInheritanceAnnotations(), ifaceClass); if (annotationMatchResult != null) { handleMatchedAnnotation(annotationMatchResult); return true; @@ -899,17 +899,18 @@ return false; } - static AnnotationMatchResult containsAnnotation( - ProguardTypeMatcher classAnnotation, DexClass clazz) { - return containsAnnotation(classAnnotation, clazz.annotations()); + static AnnotationMatchResult containsAllAnnotations( + List<ProguardTypeMatcher> annotationMatchers, DexClass clazz) { + return containsAllAnnotations(annotationMatchers, clazz.annotations()); } - static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> boolean containsAnnotation( - ProguardTypeMatcher classAnnotation, - DexEncodedMember<D, R> member, - Consumer<AnnotationMatchResult> matchedAnnotationsConsumer) { + static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> + boolean containsAllAnnotations( + List<ProguardTypeMatcher> annotationMatchers, + DexEncodedMember<D, R> member, + Consumer<AnnotationMatchResult> matchedAnnotationsConsumer) { AnnotationMatchResult annotationMatchResult = - containsAnnotation(classAnnotation, member.annotations()); + containsAllAnnotations(annotationMatchers, member.annotations()); if (annotationMatchResult != null) { matchedAnnotationsConsumer.accept(annotationMatchResult); return true; @@ -918,7 +919,7 @@ DexEncodedMethod method = member.asDexEncodedMethod(); for (int i = 0; i < method.parameterAnnotationsList.size(); i++) { annotationMatchResult = - containsAnnotation(classAnnotation, method.parameterAnnotationsList.get(i)); + containsAllAnnotations(annotationMatchers, method.parameterAnnotationsList.get(i)); if (annotationMatchResult != null) { matchedAnnotationsConsumer.accept(annotationMatchResult); return true; @@ -928,14 +929,28 @@ return false; } - private static AnnotationMatchResult containsAnnotation( - ProguardTypeMatcher classAnnotation, DexAnnotationSet annotations) { - if (classAnnotation == null) { + private static AnnotationMatchResult containsAllAnnotations( + List<ProguardTypeMatcher> annotationMatchers, DexAnnotationSet annotations) { + if (annotationMatchers.isEmpty()) { return AnnotationsIgnoredMatchResult.getInstance(); } + List<DexAnnotation> matchedAnnotations = new ArrayList<>(); + for (ProguardTypeMatcher annotationMatcher : annotationMatchers) { + DexAnnotation matchedAnnotation = + getFirstAnnotationThatMatches(annotationMatcher, annotations); + if (matchedAnnotation == null) { + return null; + } + matchedAnnotations.add(matchedAnnotation); + } + return new ConcreteAnnotationMatchResult(matchedAnnotations); + } + + private static DexAnnotation getFirstAnnotationThatMatches( + ProguardTypeMatcher annotationMatcher, DexAnnotationSet annotations) { for (DexAnnotation annotation : annotations.annotations) { - if (classAnnotation.matches(annotation.annotation.type)) { - return new ConcreteAnnotationMatchResult(annotation); + if (annotationMatcher.matches(annotation.getAnnotationType())) { + return annotation; } } return null;
diff --git a/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java index 1098bcd..dd537bc 100644 --- a/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java +++ b/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java
@@ -27,13 +27,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules); @@ -44,13 +44,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { @@ -58,13 +58,13 @@ origin, position, source, - classAnnotation, + classAnnotations, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + inheritanceAnnotations, inheritanceClassName, inheritanceIsExtends, memberRules);
diff --git a/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java b/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java index aa52ca3..0f2f684 100644 --- a/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java +++ b/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java
@@ -27,13 +27,13 @@ origin, getPosition(), source, - classAnnotation, + buildClassAnnotations(), classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + buildInheritanceAnnotations(), inheritanceClassName, inheritanceIsExtends, memberRules); @@ -44,13 +44,13 @@ Origin origin, Position position, String source, - ProguardTypeMatcher classAnnotation, + List<ProguardTypeMatcher> classAnnotations, ProguardAccessFlags classAccessFlags, ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated, ProguardClassType classType, ProguardClassNameList classNames, - ProguardTypeMatcher inheritanceAnnotation, + List<ProguardTypeMatcher> inheritanceAnnotations, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, List<ProguardMemberRule> memberRules) { @@ -58,13 +58,13 @@ origin, position, source, - classAnnotation, + classAnnotations, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames, - inheritanceAnnotation, + inheritanceAnnotations, inheritanceClassName, inheritanceIsExtends, memberRules);
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java index 4cada28..222c6a8 100644 --- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -5,6 +5,7 @@ import static com.android.tools.r8.DiagnosticsChecker.checkDiagnostics; import static com.android.tools.r8.shaking.ProguardConfigurationSourceStrings.createConfigurationForTesting; +import static com.android.tools.r8.utils.BooleanUtils.intValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertEquals; @@ -17,6 +18,8 @@ import static org.junit.Assert.fail; import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; import com.android.tools.r8.graph.ClassAccessFlags; @@ -34,10 +37,12 @@ import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; import com.android.tools.r8.utils.KeepingDiagnosticHandler; +import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -47,6 +52,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; @@ -55,6 +62,8 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; class EmptyMainClassForProguardTests { @@ -62,6 +71,7 @@ } } +@RunWith(Parameterized.class) public class ProguardConfigurationParserTest extends TestBase { private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/"; @@ -151,6 +161,13 @@ private List<String> lineSeparators = ImmutableList.of("\n", "\r\n"); private List<Character> quotes = ImmutableList.of('"', '\''); + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public ProguardConfigurationParserTest(TestParameters parameters) {} + @Before public void reset() { handler = new KeepingDiagnosticHandler(); @@ -187,7 +204,7 @@ } @Test - public void parseMultipleNamePatterns() throws Exception { + public void parseMultipleNamePatterns() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(MULTIPLE_NAME_PATTERNS_FILE)); @@ -210,7 +227,7 @@ } @Test - public void parseNonJavaIdentifiers() throws Exception { + public void parseNonJavaIdentifiers() { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, new Reporter()); @@ -250,8 +267,8 @@ assertEquals(0x03, matches); } - private void testDontXXX(String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) - throws Exception { + private void testDontXXX( + String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -265,13 +282,13 @@ } @Test - public void testDontXXX() throws Exception { + public void testDontXXX() { testDontXXX("warn", ProguardConfiguration::getDontWarnPatterns); testDontXXX("note", ProguardConfiguration::getDontNotePatterns); } private void testDontXXXMultiple( - String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) throws Exception { + String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -288,13 +305,13 @@ } @Test - public void testDontWarnMultiple() throws Exception { + public void testDontWarnMultiple() { testDontXXXMultiple("warn", ProguardConfiguration::getDontWarnPatterns); testDontXXXMultiple("note", ProguardConfiguration::getDontNotePatterns); } private void testDontXXXAllExplicitly( - String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) throws Exception { + String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -308,13 +325,13 @@ } @Test - public void testDontWarnAllExplicitly() throws Exception { + public void testDontWarnAllExplicitly() { testDontXXXAllExplicitly("warn", ProguardConfiguration::getDontWarnPatterns); testDontXXXAllExplicitly("note", ProguardConfiguration::getDontNotePatterns); } private void testDontXXXAllImplicitly( - String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) throws Exception { + String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -328,13 +345,13 @@ } @Test - public void testDontWarnAllImplicitly() throws Exception { + public void testDontWarnAllImplicitly() { testDontXXXAllImplicitly("warn", ProguardConfiguration::getDontWarnPatterns); testDontXXXAllImplicitly("note", ProguardConfiguration::getDontNotePatterns); } @Test - public void parseAccessFlags() throws Exception { + public void parseAccessFlags() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(ACCESS_FLAGS_FILE)); @@ -380,7 +397,7 @@ } @Test - public void parseWhyAreYouKeeping() throws Exception { + public void parseWhyAreYouKeeping() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(WHY_ARE_YOU_KEEPING_FILE)); @@ -395,7 +412,7 @@ } @Test - public void parseAssumeNoSideEffects() throws Exception { + public void parseAssumeNoSideEffects() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS)); @@ -408,7 +425,7 @@ } @Test - public void parseAssumeNoSideEffectsWithReturnValue() throws Exception { + public void parseAssumeNoSideEffectsWithReturnValue() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE)); @@ -477,7 +494,7 @@ } @Test - public void parseAssumeValuesWithReturnValue() throws Exception { + public void parseAssumeValuesWithReturnValue() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(ASSUME_VALUES_WITH_RETURN_VALUE)); @@ -546,7 +563,7 @@ } @Test - public void testAdaptClassStrings() throws Exception { + public void testAdaptClassStrings() { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -563,7 +580,7 @@ } @Test - public void testAdaptClassStringsMultiple() throws Exception { + public void testAdaptClassStringsMultiple() { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -584,7 +601,7 @@ } @Test - public void testAdaptClassStringsAllExplicitly() throws Exception { + public void testAdaptClassStringsAllExplicitly() { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -601,7 +618,7 @@ } @Test - public void testAdaptClassStringsAllImplicitly() throws Exception { + public void testAdaptClassStringsAllImplicitly() { DexItemFactory dexItemFactory = new DexItemFactory(); ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter); @@ -618,7 +635,7 @@ } @Test - public void testIdentifierNameString() throws Exception { + public void testIdentifierNameString() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); String config1 = @@ -656,14 +673,22 @@ }); assertEquals(1, identifierNameStrings.get(2).getClassNames().size()); assertEquals("*", identifierNameStrings.get(2).getClassNames().toString()); - identifierNameStrings.get(2).getMemberRules().forEach(memberRule -> { - assertEquals(ProguardMemberType.ALL, memberRule.getRuleType()); - assertTrue(memberRule.getAnnotation().toString().endsWith("IdentifierNameString")); - }); + identifierNameStrings + .get(2) + .getMemberRules() + .forEach( + memberRule -> { + assertEquals(ProguardMemberType.ALL, memberRule.getRuleType()); + assertEquals(1, memberRule.getAnnotations().size()); + assertTrue( + ListUtils.first(memberRule.getAnnotations()) + .toString() + .endsWith("IdentifierNameString")); + }); } @Test - public void parseDontobfuscate() throws Exception { + public void parseDontobfuscate() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(DONT_OBFUSCATE)); @@ -673,7 +698,7 @@ } @Test - public void parseRepackageClassesEmpty() throws Exception { + public void parseRepackageClassesEmpty() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(PACKAGE_OBFUSCATION_1)); @@ -685,7 +710,7 @@ } @Test - public void parseRepackageClassesNonEmpty() throws Exception { + public void parseRepackageClassesNonEmpty() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(PACKAGE_OBFUSCATION_2)); @@ -697,7 +722,7 @@ } @Test - public void parseFlattenPackageHierarchyEmpty() throws Exception { + public void parseFlattenPackageHierarchyEmpty() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(PACKAGE_OBFUSCATION_3)); @@ -709,7 +734,7 @@ } @Test - public void parseFlattenPackageHierarchyNonEmpty() throws Exception { + public void parseFlattenPackageHierarchyNonEmpty() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(PACKAGE_OBFUSCATION_4)); @@ -721,7 +746,7 @@ } @Test - public void flattenPackageHierarchyCannotOverrideRepackageClasses() throws Exception { + public void flattenPackageHierarchyCannotOverrideRepackageClasses() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); Path path = Paths.get(PACKAGE_OBFUSCATION_5); @@ -735,7 +760,7 @@ } @Test - public void repackageClassesOverridesFlattenPackageHierarchy() throws Exception { + public void repackageClassesOverridesFlattenPackageHierarchy() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); Path path = Paths.get(PACKAGE_OBFUSCATION_6); @@ -749,7 +774,7 @@ } @Test - public void parseApplyMapping() throws Exception { + public void parseApplyMapping() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(APPLY_MAPPING)); @@ -759,7 +784,7 @@ } @Test - public void parseApplyMappingWithoutFile() throws Exception { + public void parseApplyMappingWithoutFile() { Path path = Paths.get(APPLY_MAPPING_WITHOUT_FILE); try { ProguardConfigurationParser parser = @@ -781,7 +806,7 @@ } @Test - public void parseIncluding() throws Exception { + public void parseIncluding() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(Paths.get(INCLUDING)); @@ -789,7 +814,7 @@ } @Test - public void parseInvalidIncluding1() throws IOException { + public void parseInvalidIncluding1() { Path path = Paths.get(INVALID_INCLUDING_1); try { new ProguardConfigurationParser(new DexItemFactory(), reporter) @@ -1442,7 +1467,7 @@ } @Test - public void parseKeepParameterNames() throws Exception { + public void parseKeepParameterNames() { try { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); @@ -1458,7 +1483,7 @@ } @Test - public void parseKeepParameterNamesWithoutMinification() throws Exception { + public void parseKeepParameterNamesWithoutMinification() { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); parser.parse(createConfigurationForTesting(ImmutableList.of( @@ -1483,7 +1508,7 @@ } @Test - public void parseShortLine() throws IOException { + public void parseShortLine() { try { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); @@ -1496,7 +1521,7 @@ } @Test - public void parseNoLocals() throws IOException { + public void parseNoLocals() { try { ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); @@ -2915,4 +2940,239 @@ parser.parse(proguardConfig); verifyParserEndsCleanly(); } + + @Test + public void parseClassAnnotationsAndFlags() { + List<String> configurationContents = + ImmutableList.of( + // 1 annotation without flags. + "-keep @Foo class *", + // 1 annotation with public flag. + "-keep public @Foo class *", + "-keep @Foo public class *", + // 1 annotation with public final flags. + "-keep public final @Foo class *", + "-keep public @Foo final class *", + "-keep @Foo public final class *", + // 2 annotations without flags. + "-keep @Foo @Bar class *", + // 2 annotations with public flag. + "-keep public @Foo @Bar class *", + "-keep @Foo public @Bar class *", + "-keep @Foo @Bar public class *", + // 2 annotations with public final flags. + "-keep public final @Foo @Bar class *", + "-keep public @Foo final @Bar class *", + "-keep public @Foo @Bar final class *", + "-keep @Foo public final @Bar class *", + "-keep @Foo public @Bar final class *", + "-keep @Foo @Bar public final class *"); + for (String configurationContent : configurationContents) { + DexItemFactory dexItemFactory = new DexItemFactory(); + ProguardConfigurationParser parser = + new ProguardConfigurationParser(dexItemFactory, reporter); + parser.parse(createConfigurationForTesting(ImmutableList.of(configurationContent))); + verifyParserEndsCleanly(); + + ProguardConfiguration configuration = parser.getConfig(); + assertEquals(1, configuration.getRules().size()); + + ProguardKeepRule rule = ListUtils.first(configuration.getRules()).asProguardKeepRule(); + assertEquals(configurationContent.contains("final"), rule.getClassAccessFlags().isFinal()); + assertEquals(configurationContent.contains("public"), rule.getClassAccessFlags().isPublic()); + assertEquals( + 1 + intValue(configurationContent.contains("@Bar")), rule.getClassAnnotations().size()); + + ProguardTypeMatcher.MatchSpecificType fooAnnotation = + rule.getClassAnnotations().get(0).asSpecificTypeMatcher(); + assertEquals("Foo", fooAnnotation.getSpecificType().toSourceString()); + + if (configurationContent.contains("@Bar")) { + ProguardTypeMatcher.MatchSpecificType barAnnotation = + rule.getClassAnnotations().get(1).asSpecificTypeMatcher(); + assertEquals("Bar", barAnnotation.getSpecificType().toSourceString()); + } + } + } + + @Test + public void parseFieldAnnotationsAndFlags() { + Map<String, Optional<Class<? extends Exception>>> configurationContents = + ImmutableMap.<String, Optional<Class<? extends Exception>>>builder() + // 1 annotation without flags. + .put("-keep class * { @Foo Type FIELD; }", Optional.empty()) + // 1 annotation with public flag. + .put("-keep class * { public @Foo Type FIELD; }", Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo public Type FIELD; }", Optional.empty()) + // 1 annotation with public final flags. + .put( + "-keep class * { public final @Foo Type FIELD; }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { public @Foo final Type FIELD; }", + Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo public final Type FIELD; }", Optional.empty()) + //// 2 annotations without flags. + .put("-keep class * { @Foo @Bar Type FIELD; }", Optional.empty()) + //// 2 annotations with public flag. + .put( + "-keep class * { public @Foo @Bar Type FIELD; }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { @Foo public @Bar Type FIELD; }", + Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo @Bar public Type FIELD; }", Optional.empty()) + //// 2 annotations with public final flags. + .put( + "-keep class * { public final @Foo @Bar Type FIELD; }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { public @Foo final @Bar Type FIELD; }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { public @Foo @Bar final Type FIELD; }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { @Foo public final @Bar Type FIELD; }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { @Foo public @Bar final Type FIELD; }", + Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo @Bar public final Type FIELD; }", Optional.empty()) + .build(); + configurationContents.forEach( + (configurationContent, expectedExceptionClass) -> { + DexItemFactory dexItemFactory = new DexItemFactory(); + ProguardConfigurationParser parser = + new ProguardConfigurationParser(dexItemFactory, reporter); + try { + parser.parse(createConfigurationForTesting(ImmutableList.of(configurationContent))); + assertFalse(expectedExceptionClass.isPresent()); + } catch (Throwable e) { + assertTrue(expectedExceptionClass.isPresent()); + assertEquals(expectedExceptionClass.get(), e.getClass()); + reset(); + return; + } + + verifyParserEndsCleanly(); + + ProguardConfiguration configuration = parser.getConfig(); + assertEquals(1, configuration.getRules().size()); + + ProguardKeepRule rule = ListUtils.first(configuration.getRules()).asProguardKeepRule(); + assertEquals(1, rule.getMemberRules().size()); + + ProguardMemberRule memberRule = ListUtils.first(rule.getMemberRules()); + + assertEquals( + configurationContent.contains("final"), memberRule.getAccessFlags().isFinal()); + assertEquals( + configurationContent.contains("public"), memberRule.getAccessFlags().isPublic()); + assertEquals( + 1 + intValue(configurationContent.contains("@Bar")), + memberRule.getAnnotations().size()); + + ProguardTypeMatcher.MatchSpecificType fooAnnotation = + memberRule.getAnnotations().get(0).asSpecificTypeMatcher(); + assertEquals("Foo", fooAnnotation.getSpecificType().toSourceString()); + + if (configurationContent.contains("@Bar")) { + ProguardTypeMatcher.MatchSpecificType barAnnotation = + memberRule.getAnnotations().get(1).asSpecificTypeMatcher(); + assertEquals("Bar", barAnnotation.getSpecificType().toSourceString()); + } + }); + } + + @Test + public void parseMethodAnnotationsAndFlags() { + Map<String, Optional<Class<? extends Exception>>> configurationContents = + ImmutableMap.<String, Optional<Class<? extends Exception>>>builder() + // 1 annotation without flags. + .put("-keep class * { @Foo Type method(); }", Optional.empty()) + // 1 annotation with public flag. + .put( + "-keep class * { public @Foo Type method(); }", Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo public Type method(); }", Optional.empty()) + // 1 annotation with public final flags. + .put( + "-keep class * { public final @Foo Type method(); }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { public @Foo final Type method(); }", + Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo public final Type method(); }", Optional.empty()) + //// 2 annotations without flags. + .put("-keep class * { @Foo @Bar Type method(); }", Optional.empty()) + //// 2 annotations with public flag. + .put( + "-keep class * { public @Foo @Bar Type method(); }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { @Foo public @Bar Type method(); }", + Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo @Bar public Type method(); }", Optional.empty()) + //// 2 annotations with public final flags. + .put( + "-keep class * { public final @Foo @Bar Type method(); }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { public @Foo final @Bar Type method(); }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { public @Foo @Bar final Type method(); }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { @Foo public final @Bar Type method(); }", + Optional.of(RuntimeException.class)) + .put( + "-keep class * { @Foo public @Bar final Type method(); }", + Optional.of(RuntimeException.class)) + .put("-keep class * { @Foo @Bar public final Type method(); }", Optional.empty()) + .build(); + configurationContents.forEach( + (configurationContent, expectedExceptionClass) -> { + DexItemFactory dexItemFactory = new DexItemFactory(); + ProguardConfigurationParser parser = + new ProguardConfigurationParser(dexItemFactory, reporter); + try { + parser.parse(createConfigurationForTesting(ImmutableList.of(configurationContent))); + assertFalse(expectedExceptionClass.isPresent()); + } catch (Throwable e) { + assertTrue(expectedExceptionClass.isPresent()); + assertEquals(expectedExceptionClass.get(), e.getClass()); + reset(); + return; + } + + verifyParserEndsCleanly(); + + ProguardConfiguration configuration = parser.getConfig(); + assertEquals(1, configuration.getRules().size()); + + ProguardKeepRule rule = ListUtils.first(configuration.getRules()).asProguardKeepRule(); + assertEquals(1, rule.getMemberRules().size()); + + ProguardMemberRule memberRule = ListUtils.first(rule.getMemberRules()); + + assertEquals( + configurationContent.contains("final"), memberRule.getAccessFlags().isFinal()); + assertEquals( + configurationContent.contains("public"), memberRule.getAccessFlags().isPublic()); + assertEquals( + 1 + intValue(configurationContent.contains("@Bar")), + memberRule.getAnnotations().size()); + + ProguardTypeMatcher.MatchSpecificType fooAnnotation = + memberRule.getAnnotations().get(0).asSpecificTypeMatcher(); + assertEquals("Foo", fooAnnotation.getSpecificType().toSourceString()); + + if (configurationContent.contains("@Bar")) { + ProguardTypeMatcher.MatchSpecificType barAnnotation = + memberRule.getAnnotations().get(1).asSpecificTypeMatcher(); + assertEquals("Bar", barAnnotation.getSpecificType().toSourceString()); + } + }); + } } \ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleClassAnnotationsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleClassAnnotationsTest.java new file mode 100644 index 0000000..80dab7c --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleClassAnnotationsTest.java
@@ -0,0 +1,102 @@ +// Copyright (c) 2020, 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.annotations; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepWithMultipleClassAnnotationsTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public KeepWithMultipleClassAnnotationsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testA() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleClassAnnotationsTest.class) + .addKeepRules( + "-keep @" + A.class.getTypeName() + " class * {", + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testAB() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleClassAnnotationsTest.class) + .addKeepRules( + "-keep @" + A.class.getTypeName() + " @" + B.class.getTypeName() + " class * {", + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testABC() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleClassAnnotationsTest.class) + .addKeepRules( + "-keep @" + + A.class.getTypeName() + + " @" + + B.class.getTypeName() + + " @" + + C.class.getTypeName() + + " class * {", + " public static void main(java.lang.String[]);", + "}") + .allowUnusedProguardConfigurationRules() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(inspector -> assertTrue(inspector.allClasses().isEmpty())); + } + + @A + @B + static class TestClass { + + public static void main(String[] args) { + System.out.println("Hello world!"); + } + } + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE}) + @interface A {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE}) + @interface B {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE}) + @interface C {} +}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleInheritanceAnnotationsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleInheritanceAnnotationsTest.java new file mode 100644 index 0000000..25e610b --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleInheritanceAnnotationsTest.java
@@ -0,0 +1,108 @@ +// Copyright (c) 2020, 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.annotations; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepWithMultipleInheritanceAnnotationsTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public KeepWithMultipleInheritanceAnnotationsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testA() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleInheritanceAnnotationsTest.class) + .addKeepRules( + "-keep class * extends @" + A.class.getTypeName() + " * {", + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testAB() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleInheritanceAnnotationsTest.class) + .addKeepRules( + "-keep class * extends @" + + A.class.getTypeName() + + " @" + + B.class.getTypeName() + + " * {", + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testABC() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleInheritanceAnnotationsTest.class) + .addKeepRules( + "-keep class * extends @" + + A.class.getTypeName() + + " @" + + B.class.getTypeName() + + " @" + + C.class.getTypeName() + + " * {", + " public static void main(java.lang.String[]);", + "}") + .allowUnusedProguardConfigurationRules() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(inspector -> assertTrue(inspector.allClasses().isEmpty())); + } + + @A + @B + static class TestClassBase {} + + static class TestClass extends TestClassBase { + + public static void main(String[] args) { + System.out.println("Hello world!"); + } + } + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE}) + @interface A {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE}) + @interface B {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE}) + @interface C {} +}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleMemberAnnotationsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleMemberAnnotationsTest.java new file mode 100644 index 0000000..cafe2a4 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleMemberAnnotationsTest.java
@@ -0,0 +1,106 @@ +// Copyright (c) 2020, 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.annotations; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepWithMultipleMemberAnnotationsTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public KeepWithMultipleMemberAnnotationsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testA() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleMemberAnnotationsTest.class) + .addKeepRules( + "-keepclasseswithmembers class * {", + " @" + A.class.getTypeName() + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testAB() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleMemberAnnotationsTest.class) + .addKeepRules( + "-keepclasseswithmembers class * {", + " @" + + A.class.getTypeName() + + " @" + + B.class.getTypeName() + + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testABC() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleMemberAnnotationsTest.class) + .addKeepRules( + "-keepclasseswithmembers class * {", + " @" + + A.class.getTypeName() + + " @" + + B.class.getTypeName() + + " @" + + C.class.getTypeName() + + " public static void main(java.lang.String[]);", + "}") + .allowUnusedProguardConfigurationRules() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(inspector -> assertTrue(inspector.allClasses().isEmpty())); + } + + static class TestClass { + + @A + @B + public static void main(String[] args) { + System.out.println("Hello world!"); + } + } + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.METHOD}) + @interface A {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.METHOD}) + @interface B {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.METHOD}) + @interface C {} +}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleParameterAnnotationsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleParameterAnnotationsTest.java new file mode 100644 index 0000000..795574a --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/annotations/KeepWithMultipleParameterAnnotationsTest.java
@@ -0,0 +1,104 @@ +// Copyright (c) 2020, 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.annotations; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepWithMultipleParameterAnnotationsTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public KeepWithMultipleParameterAnnotationsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testA() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleParameterAnnotationsTest.class) + .addKeepRules( + "-keepclasseswithmembers class * {", + " @" + A.class.getTypeName() + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testAB() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleParameterAnnotationsTest.class) + .addKeepRules( + "-keepclasseswithmembers class * {", + " @" + + A.class.getTypeName() + + " @" + + B.class.getTypeName() + + " public static void main(java.lang.String[]);", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testABC() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(KeepWithMultipleParameterAnnotationsTest.class) + .addKeepRules( + "-keepclasseswithmembers class * {", + " @" + + A.class.getTypeName() + + " @" + + B.class.getTypeName() + + " @" + + C.class.getTypeName() + + " public static void main(java.lang.String[]);", + "}") + .allowUnusedProguardConfigurationRules() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(inspector -> assertTrue(inspector.allClasses().isEmpty())); + } + + static class TestClass { + + public static void main(@A @B String[] args) { + System.out.println("Hello world!"); + } + } + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.PARAMETER}) + @interface A {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.PARAMETER}) + @interface B {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.PARAMETER}) + @interface C {} +}