Remove annotations from non-kept items in non-compat mode
RELNOTES: Remove annotations from non-kept items in non-compat mode.
When building with R8 in Proguard compatibility mode, R8 does not remove any annotations from the program that are matched by a `-keepattributes` rule (e.g., the `RuntimeVisibleAnnnotations` attribute). In non-compatibility mode (also called full mode), R8 now removes all annotations from the program, except annotations on classes, fields, and methods that are matched by a `-keep` rule.
For example, to retain all runtime visible annotations for a class `C` it is no longer sufficient to add the rule '-keepattributes RuntimeVisibleAnnotations' in non-compatibility mode. The configuration must also explicitly add a `-keep` rule for class `C`, which can be done with the rule `-keep class C`, or the rule `-keep,allowobfuscation,allowshrinking class C`, which allows both renaming and shrinking of `C`, but still preserves the annotations of `C` if the class is not removed.
Change-Id: Ife273adbcfa1c9a690fc905566b6a87c66353737
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 1a78e63..e02ccfb 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -408,7 +408,7 @@
AnnotationRemover annotationRemover =
annotationRemoverBuilder
.build(appViewWithLiveness, removedClasses);
- annotationRemover.ensureValid().run();
+ annotationRemover.ensureValid().run(executorService);
new GenericSignatureRewriter(appView, NamingLens.getIdentityLens(), genericContextBuilder)
.run(appView.appInfo().classes(), executorService);
}
@@ -642,7 +642,7 @@
// Remove annotations that refer to types that no longer exist.
AnnotationRemover.builder()
.build(appView.withLiveness(), removedClasses)
- .run();
+ .run(executorService);
new GenericSignatureRewriter(
appView, NamingLens.getIdentityLens(), genericContextBuilder)
.run(appView.appInfo().classes(), executorService);
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 0576326..1141602 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -25,6 +25,30 @@
import java.util.function.Function;
public class DexAnnotation extends DexItem implements StructuralItem<DexAnnotation> {
+
+ public enum AnnotatedKind {
+ FIELD,
+ METHOD,
+ TYPE,
+ PARAMETER;
+
+ public static AnnotatedKind from(DexDefinition definition) {
+ return from(definition.getReference());
+ }
+
+ public static AnnotatedKind from(ProgramDefinition definition) {
+ return from(definition.getReference());
+ }
+
+ public static AnnotatedKind from(DexReference reference) {
+ return reference.apply(type -> TYPE, field -> FIELD, method -> METHOD);
+ }
+
+ public boolean isParameter() {
+ return this == PARAMETER;
+ }
+ }
+
public static final DexAnnotation[] EMPTY_ARRAY = {};
public static final int VISIBILITY_BUILD = 0x00;
public static final int VISIBILITY_RUNTIME = 0x01;
@@ -300,6 +324,11 @@
return annotation.annotation.type == factory.annotationDefault;
}
+ public static boolean isJavaLangRetentionAnnotation(
+ DexAnnotation annotation, DexItemFactory factory) {
+ return annotation.getAnnotationType() == factory.retentionType;
+ }
+
public static boolean isSourceDebugExtension(DexAnnotation annotation,
DexItemFactory factory) {
return annotation.annotation.type == factory.annotationSourceDebugExtension;
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 651a484..454be57 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -44,6 +44,18 @@
this.annotations = annotations;
}
+ public DexAnnotationSet create(DexAnnotation[] annotations) {
+ return ArrayUtils.isEmpty(annotations) ? empty() : new DexAnnotationSet(annotations);
+ }
+
+ public DexAnnotation get(int index) {
+ return annotations[index];
+ }
+
+ public DexAnnotation getFirst() {
+ return get(0);
+ }
+
@Override
public DexAnnotationSet self() {
return this;
@@ -192,14 +204,8 @@
if (isEmpty()) {
return this;
}
- DexAnnotation[] rewritten = ArrayUtils.map(DexAnnotation[].class, annotations, rewriter);
- if (rewritten == annotations) {
- return this;
- }
- if (rewritten.length == 0) {
- return DexAnnotationSet.empty();
- }
- return new DexAnnotationSet(rewritten);
+ DexAnnotation[] rewritten = ArrayUtils.map(annotations, rewriter, DexAnnotation.EMPTY_ARRAY);
+ return rewritten != annotations ? create(rewritten) : this;
}
public DexAnnotationSet methodParametersWithFakeThisArguments(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinition.java b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
index 8eeda6b..2695b1f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
@@ -3,8 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.shaking.AnnotationRemover;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -31,15 +31,14 @@
public abstract AccessFlags<?> getAccessFlags();
- public DexAnnotationSet liveAnnotations(AppView<AppInfoWithLiveness> appView) {
- return annotations.keepIf(
- annotation -> AnnotationRemover.shouldKeepAnnotation(appView, this, annotation));
- }
-
public void clearAnnotations() {
setAnnotations(DexAnnotationSet.empty());
}
+ public void clearAllAnnotations() {
+ clearAnnotations();
+ }
+
public void setAnnotations(DexAnnotationSet annotations) {
this.annotations = annotations;
}
@@ -48,6 +47,12 @@
setAnnotations(annotations().removeIf(predicate));
}
+ public void rewriteAllAnnotations(
+ BiFunction<DexAnnotation, AnnotatedKind, DexAnnotation> rewriter) {
+ setAnnotations(
+ annotations().rewrite(annotation -> rewriter.apply(annotation, AnnotatedKind.from(this))));
+ }
+
public boolean isDexClass() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
index 98df8c7..e338ef5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -112,7 +112,7 @@
Function<DexAnnotationElement, DexAnnotationElement> elementRewriter) {
DexType rewrittenType = typeRewriter.apply(type);
DexAnnotationElement[] rewrittenElements =
- ArrayUtils.map(DexAnnotationElement[].class, elements, elementRewriter);
+ ArrayUtils.map(elements, elementRewriter, DexAnnotationElement.EMPTY_ARRAY);
if (rewrittenType == type && rewrittenElements == elements) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 449747b..f8a2537 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -44,6 +44,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.code.IRCode;
@@ -72,8 +73,6 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.shaking.AnnotationRemover;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -92,6 +91,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntPredicate;
@@ -399,11 +399,6 @@
return getReference().proto.returnType;
}
- public ParameterAnnotationsList liveParameterAnnotations(AppView<AppInfoWithLiveness> appView) {
- return parameterAnnotationsList.keepIf(
- annotation -> AnnotationRemover.shouldKeepAnnotation(appView, this, annotation));
- }
-
public OptionalBool isLibraryMethodOverride() {
return isNonPrivateVirtualMethod() ? isLibraryMethodOverride : OptionalBool.FALSE;
}
@@ -894,14 +889,38 @@
return builder.toString();
}
- public ParameterAnnotationsList getParameterAnnotations() {
- return parameterAnnotationsList;
+ @Override
+ public void clearAllAnnotations() {
+ clearAnnotations();
+ clearParameterAnnotations();
+ }
+
+ @Override
+ public void rewriteAllAnnotations(
+ BiFunction<DexAnnotation, AnnotatedKind, DexAnnotation> rewriter) {
+ setAnnotations(
+ annotations().rewrite(annotation -> rewriter.apply(annotation, AnnotatedKind.METHOD)));
+ setParameterAnnotations(
+ getParameterAnnotations()
+ .rewrite(annotation -> rewriter.apply(annotation, AnnotatedKind.PARAMETER)));
}
public void clearParameterAnnotations() {
parameterAnnotationsList = ParameterAnnotationsList.empty();
}
+ public DexAnnotationSet getParameterAnnotation(int index) {
+ return getParameterAnnotations().get(index);
+ }
+
+ public ParameterAnnotationsList getParameterAnnotations() {
+ return parameterAnnotationsList;
+ }
+
+ public void setParameterAnnotations(ParameterAnnotationsList parameterAnnotations) {
+ this.parameterAnnotationsList = parameterAnnotations;
+ }
+
public String toSmaliString(ClassNameMapper naming) {
checkIfObsolete();
StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index ca8ba66..d3e536e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -451,6 +451,8 @@
createStaticallyKnownType("Ljava/util/function/LongConsumer;");
public final DexType intConsumer = createStaticallyKnownType("Ljava/util/function/IntConsumer;");
+ public final DexType retentionType =
+ createStaticallyKnownType("Ljava/lang/annotation/Retention;");
public final DexType runtimeExceptionType = createStaticallyKnownType(runtimeExceptionDescriptor);
public final DexType throwableType = createStaticallyKnownType(throwableDescriptor);
public final DexType illegalAccessErrorType =
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
index dee2c8d..ba98190 100644
--- a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
+++ b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
@@ -70,6 +70,13 @@
this.missingParameterAnnotations = missingParameterAnnotations;
}
+ public static ParameterAnnotationsList create(
+ DexAnnotationSet[] values, int missingParameterAnnotations) {
+ return ArrayUtils.isEmpty(values)
+ ? empty()
+ : new ParameterAnnotationsList(values, missingParameterAnnotations);
+ }
+
@Override
public ParameterAnnotationsList self() {
return this;
@@ -226,11 +233,15 @@
return new ParameterAnnotationsList(filtered, missingParameterAnnotations);
}
- public ParameterAnnotationsList rewrite(Function<DexAnnotationSet, DexAnnotationSet> mapper) {
- DexAnnotationSet[] rewritten = ArrayUtils.map(DexAnnotationSet[].class, values, mapper);
- if (rewritten != values) {
- return new ParameterAnnotationsList(rewritten, missingParameterAnnotations);
+ public ParameterAnnotationsList rewrite(Function<DexAnnotation, DexAnnotation> mapper) {
+ if (isEmpty()) {
+ return this;
}
- return this;
+ DexAnnotationSet[] rewritten =
+ ArrayUtils.map(
+ values, annotations -> annotations.rewrite(mapper), DexAnnotationSet.EMPTY_ARRAY);
+ return rewritten != values
+ ? ParameterAnnotationsList.create(rewritten, missingParameterAnnotations)
+ : this;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
index 17e779e..c1dedd5 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
+import java.util.function.BiFunction;
import java.util.function.Function;
public interface ProgramDefinition extends Definition, ProgramDerivedContext {
@@ -21,6 +23,15 @@
return this;
}
+ default void clearAllAnnotations() {
+ getDefinition().clearAllAnnotations();
+ }
+
+ default void rewriteAllAnnotations(
+ BiFunction<DexAnnotation, AnnotatedKind, DexAnnotation> rewriter) {
+ getDefinition().rewriteAllAnnotations(rewriter);
+ }
+
DexProgramClass getContextClass();
AccessFlags<?> getAccessFlags();
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramField.java b/src/main/java/com/android/tools/r8/graph/ProgramField.java
index 3e2a276..aec6be0 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramField.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramField.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
+import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
public class ProgramField extends DexClassAndField
implements ProgramMember<DexEncodedField, DexField> {
@@ -60,12 +60,7 @@
}
@Override
- public KotlinMemberLevelInfo getKotlinInfo() {
+ public KotlinFieldLevelInfo getKotlinInfo() {
return getDefinition().getKotlinInfo();
}
-
- @Override
- public void clearKotlinInfo() {
- getDefinition().clearKotlinInfo();
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMember.java b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
index 1234378..fa77682 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMember.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
@@ -23,5 +23,11 @@
KotlinMemberLevelInfo getKotlinInfo();
- void clearKotlinInfo();
+ default void clearGenericSignature() {
+ getDefinition().clearGenericSignature();
+ }
+
+ default void clearKotlinInfo() {
+ getDefinition().clearKotlinInfo();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 5a58189..5faf268 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
-import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
+import com.android.tools.r8.kotlin.KotlinMethodLevelInfo;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
@@ -95,15 +95,10 @@
}
@Override
- public KotlinMemberLevelInfo getKotlinInfo() {
+ public KotlinMethodLevelInfo getKotlinInfo() {
return getDefinition().getKotlinInfo();
}
- @Override
- public void clearKotlinInfo() {
- getDefinition().clearKotlinInfo();
- }
-
public MethodPosition getPosition() {
return getDefinition().getPosition();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
index 5b058c9..191adc4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
@@ -111,9 +111,9 @@
private DexProto rewriteProto(DexProto proto, Map<DexType, MergeGroup> groups) {
DexType[] parameters =
ArrayUtils.map(
- DexType[].class,
proto.getParameters().values,
- parameter -> rewriteType(parameter, groups));
+ parameter -> rewriteType(parameter, groups),
+ DexType.EMPTY_ARRAY);
return dexItemFactory.createProto(rewriteType(proto.getReturnType(), groups), parameters);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java b/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
index 4fdad6a..fedb75d 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
@@ -40,10 +40,8 @@
}
private void processMethod(DexEncodedMethod method) {
- method.setAnnotations(method.annotations().rewrite(this::rewriteAnnotation));
- method.parameterAnnotationsList =
- method.parameterAnnotationsList.rewrite(
- dexAnnotationSet -> dexAnnotationSet.rewrite(this::rewriteAnnotation));
+ method.rewriteAllAnnotations(
+ (annotation, isParameterAnnotation) -> rewriteAnnotation(annotation));
}
private void processField(DexEncodedField field) {
@@ -73,7 +71,7 @@
if (value.isDexValueArray()) {
DexValue[] originalValues = value.asDexValueArray().getValues();
DexValue[] rewrittenValues =
- ArrayUtils.map(DexValue[].class, originalValues, this::rewriteComplexValue);
+ ArrayUtils.map(originalValues, this::rewriteComplexValue, DexValue.EMPTY_ARRAY);
if (rewrittenValues != originalValues) {
return new DexValueArray(rewrittenValues);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 567b7cb..ac1ad75 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -6,24 +6,28 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedAnnotation;
-import com.android.tools.r8.graph.DexEncodedMember;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
import com.android.tools.r8.kotlin.KotlinPropertyInfo;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
public class AnnotationRemover {
@@ -49,49 +53,50 @@
}
/** Used to filter annotations on classes, methods and fields. */
- private boolean filterAnnotations(DexDefinition holder, DexAnnotation annotation) {
+ private boolean filterAnnotations(
+ ProgramDefinition holder, DexAnnotation annotation, AnnotatedKind kind) {
return annotationsToRetain.contains(annotation)
- || shouldKeepAnnotation(appView, holder, annotation, isAnnotationTypeLive(annotation));
- }
-
- public static boolean shouldKeepAnnotation(
- AppView<AppInfoWithLiveness> appView, DexDefinition holder, DexAnnotation annotation) {
- return shouldKeepAnnotation(
- appView, holder, annotation, isAnnotationTypeLive(annotation, appView));
+ || shouldKeepAnnotation(
+ appView, holder, annotation, isAnnotationTypeLive(annotation), kind);
}
public static boolean shouldKeepAnnotation(
AppView<?> appView,
- DexDefinition holder,
+ ProgramDefinition holder,
DexAnnotation annotation,
- boolean isAnnotationTypeLive) {
+ boolean isAnnotationTypeLive,
+ AnnotatedKind kind) {
// If we cannot run the AnnotationRemover we are keeping the annotation.
if (!appView.options().isShrinking()) {
return true;
}
+
+ InternalOptions options = appView.options();
ProguardKeepAttributes config =
- appView.options().getProguardConfiguration() != null
- ? appView.options().getProguardConfiguration().getKeepAttributes()
+ options.getProguardConfiguration() != null
+ ? options.getProguardConfiguration().getKeepAttributes()
: ProguardKeepAttributes.fromPatterns(ImmutableList.of());
DexItemFactory dexItemFactory = appView.dexItemFactory();
-
switch (annotation.visibility) {
case DexAnnotation.VISIBILITY_SYSTEM:
+ if (kind.isParameter()) {
+ return false;
+ }
// InnerClass and EnclosingMember are represented in class attributes, not annotations.
assert !DexAnnotation.isInnerClassAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isMemberClassesAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingMethodAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingClassAnnotation(annotation, dexItemFactory);
- assert appView.options().passthroughDexCode
+ assert options.passthroughDexCode
|| !DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory);
if (config.exceptions && DexAnnotation.isThrowingAnnotation(annotation, dexItemFactory)) {
return true;
}
if (DexAnnotation.isSourceDebugExtension(annotation, dexItemFactory)) {
- assert holder.isDexClass();
+ assert holder.isProgramClass();
appView.setSourceDebugExtensionForType(
- holder.asDexClass(), annotation.annotation.elements[0].value.asDexValueString());
+ holder.asProgramClass(), annotation.annotation.elements[0].value.asDexValueString());
return config.sourceDebugExtension;
}
if (config.methodParameters
@@ -106,14 +111,35 @@
return false;
case DexAnnotation.VISIBILITY_RUNTIME:
- if (!config.runtimeVisibleAnnotations) {
- return false;
+ // We always keep the @java.lang.Retention annotation on annotation classes, since the
+ // removal of this annotation may change the annotation from being runtime visible to
+ // runtime invisible.
+ if (holder.isProgramClass()
+ && holder.asProgramClass().isAnnotation()
+ && DexAnnotation.isJavaLangRetentionAnnotation(annotation, dexItemFactory)) {
+ return true;
+ }
+
+ if (kind.isParameter()) {
+ if (!options.isKeepRuntimeVisibleParameterAnnotationsEnabled()) {
+ return false;
+ }
+ } else {
+ if (!options.isKeepRuntimeVisibleAnnotationsEnabled()) {
+ return false;
+ }
}
return isAnnotationTypeLive;
case DexAnnotation.VISIBILITY_BUILD:
- if (!config.runtimeInvisibleAnnotations) {
- return false;
+ if (kind.isParameter()) {
+ if (!options.isKeepRuntimeInvisibleParameterAnnotationsEnabled()) {
+ return false;
+ }
+ } else {
+ if (!options.isKeepRuntimeInvisibleAnnotationsEnabled()) {
+ return false;
+ }
}
return isAnnotationTypeLive;
@@ -132,60 +158,35 @@
return appView.appInfo().isNonProgramTypeOrLiveProgramType(annotationType);
}
- /**
- * Used to filter annotations on parameters.
- */
- private boolean filterParameterAnnotations(DexAnnotation annotation) {
- if (annotationsToRetain.contains(annotation)) {
- return true;
- }
- switch (annotation.visibility) {
- case DexAnnotation.VISIBILITY_SYSTEM:
- return false;
- case DexAnnotation.VISIBILITY_RUNTIME:
- if (!keep.runtimeVisibleParameterAnnotations) {
- return false;
- }
- break;
- case DexAnnotation.VISIBILITY_BUILD:
- if (!keep.runtimeInvisibleParameterAnnotations) {
- return false;
- }
- break;
- default:
- throw new Unreachable("Unexpected annotation visibility.");
- }
- return isAnnotationTypeLive(annotation);
- }
-
public AnnotationRemover ensureValid() {
keep.ensureValid(appView.options().forceProguardCompatibility);
return this;
}
- public void run() {
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- stripAttributes(clazz);
- clazz.setAnnotations(
- clazz.annotations().rewrite(annotation -> rewriteAnnotation(clazz, annotation)));
- // Kotlin metadata for classes are removed in the KotlinMetadataEnqueuerExtension. Kotlin
- // properties are split over fields and methods. Check if any is pinned before pruning the
- // information.
- Set<KotlinPropertyInfo> pinnedKotlinProperties = Sets.newIdentityHashSet();
- clazz.forEachProgramMember(
- member -> processMember(member.getDefinition(), clazz, pinnedKotlinProperties));
- clazz.forEachProgramMember(
- member -> {
- KotlinMemberLevelInfo kotlinInfo = member.getKotlinInfo();
- if (kotlinInfo.isProperty()
- && !pinnedKotlinProperties.contains(kotlinInfo.asProperty())) {
- member.clearKotlinInfo();
- }
- });
- }
+ public void run(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(appView.appInfo().classes(), this::run, executorService);
assert verifyNoKeptKotlinMembersForClassesWithNoKotlinInfo();
}
+ private void run(DexProgramClass clazz) {
+ KeepClassInfo keepInfo = appView.getKeepInfo().getClassInfo(clazz);
+ removeAnnotations(clazz, keepInfo);
+ stripAttributes(clazz, keepInfo);
+ // Kotlin metadata for classes are removed in the KotlinMetadataEnqueuerExtension. Kotlin
+ // properties are split over fields and methods. Check if any is pinned before pruning the
+ // information.
+ Set<KotlinPropertyInfo> pinnedKotlinProperties = Sets.newIdentityHashSet();
+ clazz.forEachProgramMember(member -> processMember(member, clazz, pinnedKotlinProperties));
+ clazz.forEachProgramMember(
+ member -> {
+ KotlinMemberLevelInfo kotlinInfo = member.getKotlinInfo();
+ if (kotlinInfo.isProperty()
+ && !pinnedKotlinProperties.contains(kotlinInfo.asProperty())) {
+ member.clearKotlinInfo();
+ }
+ });
+ }
+
private boolean verifyNoKeptKotlinMembersForClassesWithNoKotlinInfo() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (clazz.getKotlinInfo().isNoKotlinInformation()) {
@@ -200,17 +201,11 @@
}
private void processMember(
- DexEncodedMember<?, ?> member,
+ ProgramMember<?, ?> member,
DexProgramClass clazz,
Set<KotlinPropertyInfo> pinnedKotlinProperties) {
- member.setAnnotations(
- member.annotations().rewrite(annotation -> rewriteAnnotation(member, annotation)));
- if (member.isDexEncodedMethod()) {
- DexEncodedMethod method = member.asDexEncodedMethod();
- method.parameterAnnotationsList =
- method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations);
- }
- KeepMemberInfo<?, ?> memberInfo = appView.getKeepInfo().getMemberInfo(member, clazz);
+ KeepMemberInfo<?, ?> memberInfo = appView.getKeepInfo().getMemberInfo(member);
+ removeAnnotations(member, memberInfo);
if (memberInfo.isSignatureAttributeRemovalAllowed(options)) {
member.clearGenericSignature();
}
@@ -223,9 +218,10 @@
}
}
- private DexAnnotation rewriteAnnotation(DexDefinition holder, DexAnnotation original) {
+ private DexAnnotation rewriteAnnotation(
+ ProgramDefinition holder, DexAnnotation original, AnnotatedKind kind) {
// Check if we should keep this annotation first.
- if (filterAnnotations(holder, original)) {
+ if (filterAnnotations(holder, original, kind)) {
// Then, filter out values that refer to dead definitions.
return original.rewrite(this::rewriteEncodedAnnotation);
}
@@ -265,12 +261,28 @@
return liveGetter ? original : null;
}
- private void stripAttributes(DexProgramClass clazz) {
+ private void removeAnnotations(ProgramDefinition definition, KeepInfo<?, ?> keepInfo) {
+ boolean isAnnotation =
+ definition.isProgramClass() && definition.asProgramClass().isAnnotation();
+ if ((options.isForceProguardCompatibilityEnabled() || keepInfo.isPinned())) {
+ definition.rewriteAllAnnotations(
+ (annotation, kind) -> rewriteAnnotation(definition, annotation, kind));
+ } else if (!isAnnotation) {
+ definition.clearAllAnnotations();
+ } else {
+ definition.rewriteAllAnnotations(
+ (annotation, isParameterAnnotation) ->
+ DexAnnotation.isJavaLangRetentionAnnotation(annotation, appView.dexItemFactory())
+ ? annotation
+ : null);
+ }
+ }
+
+ private void stripAttributes(DexProgramClass clazz, KeepClassInfo keepInfo) {
// If [clazz] is mentioned by a keep rule, it could be used for reflection, and we therefore
// need to keep the enclosing method and inner classes attributes, if requested. In Proguard
// compatibility mode we keep these attributes independent of whether the given class is kept.
// In full mode we remove the attribute if not both sides are kept.
- KeepClassInfo keepInfo = appView.getKeepInfo().getClassInfo(clazz);
clazz.removeEnclosingMethodAttribute(
enclosingMethodAttribute ->
keepInfo.isEnclosingMethodAttributeRemovalAllowed(
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 339a56d..4378c0f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.ClasspathOrLibraryDefinition;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
@@ -169,11 +170,11 @@
/**
* Approximates the runtime dependencies for the given set of roots.
- * <p>
+ *
* <p>The implementation filters the static call-graph with liveness information on classes to
* remove virtual methods that are reachable by their static type but are unreachable at runtime as
* they are not visible from any instance.
- * <p>
+ *
* <p>As result of the analysis, an instance of {@link AppInfoWithLiveness} is returned. See the
* field descriptions for details.
*/
@@ -393,6 +394,13 @@
private final Map<DexType, Map<DexAnnotation, List<ProgramDefinition>>> deferredAnnotations =
new IdentityHashMap<>();
+ /**
+ * A map from annotation classes to parameter annotations that need to be processed should the
+ * classes ever become live.
+ */
+ private final Map<DexType, Map<DexAnnotation, List<ProgramDefinition>>>
+ deferredParameterAnnotations = new IdentityHashMap<>();
+
/** Map of active if rules to speed up aapt2 generated keep rules. */
private Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules;
@@ -1828,16 +1836,9 @@
// If this type has deferred annotations, we have to process those now, too.
if (clazz.isAnnotation()) {
- Map<DexAnnotation, List<ProgramDefinition>> annotations =
- deferredAnnotations.remove(clazz.getType());
- if (annotations != null) {
- assert annotations.keySet().stream()
- .allMatch(a -> a.getAnnotationType() == clazz.getType());
- annotations.forEach(
- (annotation, annotatedItems) ->
- annotatedItems.forEach(
- annotatedItem -> processAnnotation(annotatedItem, annotation)));
- }
+ processDeferredAnnotations(clazz, deferredAnnotations, AnnotatedKind::from);
+ processDeferredAnnotations(
+ clazz, deferredParameterAnnotations, annotatedItem -> AnnotatedKind.PARAMETER);
}
rootSet.forEachDependentInstanceConstructor(
@@ -1849,6 +1850,24 @@
analyses.forEach(analysis -> analysis.processNewlyLiveClass(clazz, workList));
}
+ private void processDeferredAnnotations(
+ DexProgramClass clazz,
+ Map<DexType, Map<DexAnnotation, List<ProgramDefinition>>> deferredAnnotations,
+ Function<ProgramDefinition, AnnotatedKind> kindProvider) {
+ Map<DexAnnotation, List<ProgramDefinition>> annotations =
+ deferredAnnotations.remove(clazz.getType());
+ if (annotations != null) {
+ assert annotations.keySet().stream()
+ .allMatch(annotation -> annotation.getAnnotationType() == clazz.getType());
+ annotations.forEach(
+ (annotation, annotatedItems) ->
+ annotatedItems.forEach(
+ annotatedItem ->
+ processAnnotation(
+ annotatedItem, annotation, kindProvider.apply(annotatedItem))));
+ }
+ }
+
private void ensureMethodsContinueToWidenAccess(ClassDefinition clazz) {
assert !clazz.isProgramClass();
ScopedDexMethodSet seen =
@@ -1931,27 +1950,35 @@
}
private void processAnnotations(ProgramDefinition annotatedItem) {
- processAnnotations(annotatedItem, annotatedItem.getDefinition().annotations());
+ processAnnotations(
+ annotatedItem,
+ annotatedItem.getDefinition().annotations(),
+ AnnotatedKind.from(annotatedItem));
}
- private void processAnnotations(ProgramDefinition annotatedItem, DexAnnotationSet annotations) {
- processAnnotations(annotatedItem, annotations.annotations);
+ private void processAnnotations(
+ ProgramDefinition annotatedItem, DexAnnotationSet annotations, AnnotatedKind kind) {
+ processAnnotations(annotatedItem, annotations.annotations, kind);
}
- private void processAnnotations(ProgramDefinition annotatedItem, DexAnnotation[] annotations) {
+ private void processAnnotations(
+ ProgramDefinition annotatedItem, DexAnnotation[] annotations, AnnotatedKind kind) {
for (DexAnnotation annotation : annotations) {
- processAnnotation(annotatedItem, annotation);
+ processAnnotation(annotatedItem, annotation, kind);
}
}
- private void processAnnotation(ProgramDefinition annotatedItem, DexAnnotation annotation) {
+ private void processAnnotation(
+ ProgramDefinition annotatedItem, DexAnnotation annotation, AnnotatedKind kind) {
DexType type = annotation.getAnnotationType();
DexClass clazz = definitionFor(type, annotatedItem);
boolean annotationTypeIsLibraryClass = clazz == null || clazz.isNotProgramClass();
boolean isLive = annotationTypeIsLibraryClass || liveTypes.contains(clazz.asProgramClass());
- if (!shouldKeepAnnotation(appView, annotatedItem.getDefinition(), annotation, isLive)) {
+ if (!shouldKeepAnnotation(appView, annotatedItem, annotation, isLive, kind)) {
// Remember this annotation for later.
if (!annotationTypeIsLibraryClass) {
+ Map<DexType, Map<DexAnnotation, List<ProgramDefinition>>> deferredAnnotations =
+ kind.isParameter() ? deferredParameterAnnotations : this.deferredAnnotations;
Map<DexAnnotation, List<ProgramDefinition>> deferredAnnotationsForAnnotationType =
deferredAnnotations.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
deferredAnnotationsForAnnotationType
@@ -3915,7 +3942,8 @@
method
.getDefinition()
.getParameterAnnotations()
- .forEachAnnotation(annotation -> processAnnotation(method, annotation));
+ .forEachAnnotation(
+ annotation -> processAnnotation(method, annotation, AnnotatedKind.PARAMETER));
}
private void traceNonDesugaredCode(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index 07f625d..e509230 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -22,5 +22,11 @@
boolean isKeepInnerClassesAttributeEnabled();
+ boolean isKeepRuntimeInvisibleAnnotationsEnabled();
+
+ boolean isKeepRuntimeInvisibleParameterAnnotationsEnabled();
+
boolean isKeepRuntimeVisibleAnnotationsEnabled();
+
+ boolean isKeepRuntimeVisibleParameterAnnotationsEnabled();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 7e9b166..ddd5bfc 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.KeepFieldInfo.Joiner;
import com.android.tools.r8.utils.InternalOptions;
@@ -86,6 +87,10 @@
return clazz == null ? keepInfoForNonProgramClass() : getClassInfo(clazz);
}
+ public final KeepMemberInfo<?, ?> getMemberInfo(ProgramMember<?, ?> member) {
+ return getMemberInfo(member.getDefinition(), member.getHolder());
+ }
+
public final KeepMethodInfo getMethodInfo(ProgramMethod method) {
return getMethodInfo(method.getDefinition(), method.getHolder());
}
diff --git a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
index 4fc6914..2616514 100644
--- a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
@@ -80,6 +80,10 @@
clazz.cast(Array.newInstance(clazz.getComponentType(), filtered.size())));
}
+ public static <T> boolean isEmpty(T[] array) {
+ return array.length == 0;
+ }
+
public static boolean isSorted(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
assert array[i] < array[i + 1];
@@ -94,18 +98,6 @@
/**
* Rewrites the input array based on the given function.
*
- * @param clazz target type's Class to cast
- * @param original an array of original elements
- * @param mapper a mapper that rewrites an original element to a new one, maybe `null`
- * @return an array with written elements
- */
- public static <T> T[] map(Class<T[]> clazz, T[] original, Function<T, T> mapper) {
- return map(original, mapper, clazz.cast(Array.newInstance(clazz.getComponentType(), 0)));
- }
-
- /**
- * Rewrites the input array based on the given function.
- *
* @param original an array of original elements
* @param mapper a mapper that rewrites an original element to a new one, maybe `null`
* @param emptyArray an empty array
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 89303ae..b75ac5a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -618,10 +618,25 @@
}
@Override
+ public boolean isKeepRuntimeInvisibleAnnotationsEnabled() {
+ return proguardConfiguration.getKeepAttributes().runtimeInvisibleAnnotations;
+ }
+
+ @Override
+ public boolean isKeepRuntimeInvisibleParameterAnnotationsEnabled() {
+ return proguardConfiguration.getKeepAttributes().runtimeInvisibleParameterAnnotations;
+ }
+
+ @Override
public boolean isKeepRuntimeVisibleAnnotationsEnabled() {
return proguardConfiguration.getKeepAttributes().runtimeVisibleAnnotations;
}
+ @Override
+ public boolean isKeepRuntimeVisibleParameterAnnotationsEnabled() {
+ return proguardConfiguration.getKeepAttributes().runtimeVisibleParameterAnnotations;
+ }
+
/**
* If any non-static class merging is enabled, information about types referred to by instanceOf
* and check cast instructions needs to be collected.
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index e9e7a58..064d98f 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -7,6 +7,7 @@
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
import lambdadesugaringnplus.other.ClassWithDefaultPackagePrivate;
import lambdadesugaringnplus.other.InterfaceWithDefaultPackagePrivate;
@@ -372,8 +373,15 @@
// I don't know how to keep this method moved to the companion class
// without the direct call.
AnnotatedInterface.annotatedStaticMethod();
- if (checkAnnotationValue(
- getCompanionClassOrInterface().getMethod("annotatedStaticMethod").getAnnotations(), 4)) {
+ Method annotatedStaticMethod =
+ getCompanionClassOrInterface().getMethod("annotatedStaticMethod");
+ if (checkAnnotationValue(annotatedStaticMethod.getAnnotations(), 4)) {
+ System.out.println("Check 4: OK");
+ } else if (isR8()
+ && !isProguardCompatibilityMode()
+ && annotatedStaticMethod.getAnnotations().length == 0) {
+ // There is currently no way to retain annotations on static interface methods, since we
+ // drop -keep rules for such methods.
System.out.println("Check 4: OK");
} else {
System.out.println("Check 4: NOT OK");
@@ -515,4 +523,12 @@
B62168701.test();
B78901754.test();
}
+
+ public static boolean isR8() {
+ return false;
+ }
+
+ public static boolean isProguardCompatibilityMode() {
+ return false;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 1a9800a..f13a704 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -39,7 +39,7 @@
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
-import java.util.function.UnaryOperator;
+import java.util.function.Consumer;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
@@ -171,10 +171,10 @@
D8Command.Builder builder = D8Command.builder();
addClasspathReference(testJarFile, builder);
for (String inputFile : inputFiles) {
- builder = builder.addProgramFiles(Paths.get(inputFile));
+ builder.addProgramFiles(Paths.get(inputFile));
}
- for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
- builder = transformation.apply(builder);
+ for (Consumer<D8Command.Builder> transformation : builderTransformations) {
+ transformation.accept(builder);
}
if (outputPath != null) {
builder.setOutput(outputPath, outputMode);
@@ -207,8 +207,8 @@
for (ProgramResource dexFile : dexFiles) {
builder.addDexProgramData(readResource(dexFile), dexFile.getOrigin());
}
- for (UnaryOperator<Builder> transformation : builderTransformations) {
- builder = transformation.apply(builder);
+ for (Consumer<D8Command.Builder> transformation : builderTransformations) {
+ transformation.accept(builder);
}
if (outputPath != null) {
builder.setOutput(outputPath, outputMode);
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 1bf86e3..b4cc992 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.OffOrAuto;
import java.nio.file.Path;
-import java.util.function.UnaryOperator;
+import java.util.function.Consumer;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
@@ -34,8 +34,8 @@
@Override
void build(Path inputFile, Path out, OutputMode mode) throws CompilationFailedException {
D8Command.Builder builder = D8Command.builder().setOutput(out, mode);
- for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
- builder = transformation.apply(builder);
+ for (Consumer<D8Command.Builder> transformation : builderTransformations) {
+ transformation.accept(builder);
}
builder.addLibraryFiles(
ToolHelper.getAndroidJar(
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index eebc9fe..ba0ced5 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -26,7 +26,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.function.UnaryOperator;
+import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,16 +41,24 @@
"-allowaccessmodification"
);
- private static final ArrayList<String> PROGUARD_OPTIONS_N_PLUS =
- Lists.newArrayList(
- "-keepclasseswithmembers public class * {",
- " public static void main(java.lang.String[]);",
- "}",
- "-keepclasseswithmembers interface **$AnnotatedInterface { <methods>; }",
- "-neverinline interface **$AnnotatedInterface { static void annotatedStaticMethod(); }",
- "-keepattributes *Annotation*",
- "-dontobfuscate",
- "-allowaccessmodification");
+ private static ArrayList<String> getProguardOptionsNPlus(
+ boolean enableProguardCompatibilityMode) {
+ return Lists.newArrayList(
+ "-keepclasseswithmembers public class * {",
+ " public static void main(java.lang.String[]);",
+ "}",
+ "-keepclasseswithmembers interface **$AnnotatedInterface { <methods>; }",
+ "-neverinline interface **$AnnotatedInterface { static void annotatedStaticMethod(); }",
+ "-keepattributes *Annotation*",
+ "-dontobfuscate",
+ "-allowaccessmodification",
+ "-assumevalues class lambdadesugaringnplus.LambdasWithStaticAndDefaultMethods {",
+ " public static boolean isR8() return true;",
+ " public static boolean isProguardCompatibilityMode() return "
+ + enableProguardCompatibilityMode
+ + ";",
+ "}");
+ }
private static Map<DexVm.Version, List<String>> alsoFailsOn =
ImmutableMap.<DexVm.Version, List<String>>builder()
@@ -161,22 +169,37 @@
@Override
@Test
public void lambdaDesugaringNPlus() throws Throwable {
+ lambdaDesugaringNPlus(false);
+ }
+
+ @Test
+ public void lambdaDesugaringNPlusCompat() throws Throwable {
+ lambdaDesugaringNPlus(true);
+ }
+
+ private void lambdaDesugaringNPlus(boolean enableProguardCompatibilityMode) throws Throwable {
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+ .withProguardCompatibilityMode(enableProguardCompatibilityMode)
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
- b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ b ->
+ b.addProguardConfiguration(
+ getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
.withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus"))
.run();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+ .withProguardCompatibilityMode(enableProguardCompatibilityMode)
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
- b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ b ->
+ b.addProguardConfiguration(
+ getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
.withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
.run();
}
@@ -184,22 +207,39 @@
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
public void lambdaDesugaringNPlusWithDefaultMethods() throws Throwable {
+ lambdaDesugaringNPlusWithDefaultMethods(false);
+ }
+
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void lambdaDesugaringNPlusWithDefaultMethodsCompat() throws Throwable {
+ lambdaDesugaringNPlusWithDefaultMethods(true);
+ }
+
+ private void lambdaDesugaringNPlusWithDefaultMethods(boolean enableProguardCompatibilityMode)
+ throws Throwable {
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+ .withProguardCompatibilityMode(enableProguardCompatibilityMode)
.withMinApiLevel(AndroidApiLevel.N)
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
- b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ b ->
+ b.addProguardConfiguration(
+ getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
.withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus"))
.run();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+ .withProguardCompatibilityMode(enableProguardCompatibilityMode)
.withMinApiLevel(AndroidApiLevel.N)
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
- b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ b ->
+ b.addProguardConfiguration(
+ getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
.withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
.run();
}
@@ -236,6 +276,8 @@
class R8TestRunner extends TestRunner<R8TestRunner> {
+ private boolean enableProguardCompatibilityMode = false;
+
R8TestRunner(String testName, String packageName, String mainClass) {
super(testName, packageName, mainClass);
}
@@ -253,11 +295,18 @@
.addProguardConfiguration(ImmutableList.of("-keepattributes *"), Origin.unknown()));
}
+ public R8TestRunner withProguardCompatibilityMode(boolean enableProguardCompatibilityMode) {
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
+ return this;
+ }
+
@Override
void build(Path inputFile, Path out, OutputMode mode) throws Throwable {
- R8Command.Builder builder = R8Command.builder().setOutput(out, mode);
- for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
- builder = transformation.apply(builder);
+ CompatProguardCommandBuilder builder =
+ new CompatProguardCommandBuilder(enableProguardCompatibilityMode);
+ builder.setOutput(out, mode);
+ for (Consumer<R8Command.Builder> transformation : builderTransformations) {
+ transformation.accept(builder);
}
builder.addLibraryFiles(ToolHelper.getAndroidJar(
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index bd9bd27..1dd7194 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -49,7 +49,6 @@
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.zip.ZipFile;
import org.junit.Assert;
@@ -74,7 +73,7 @@
final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
final List<Consumer<CodeInspector>> dexInspectorChecks = new ArrayList<>();
- final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
+ final List<Consumer<B>> builderTransformations = new ArrayList<>();
TestRunner(String testName, String packageName, String mainClass) {
this.testName = testName;
@@ -131,7 +130,6 @@
} else {
fail("Unexpected builder type: " + builder.getClass());
}
- return builder;
});
}
@@ -143,7 +141,7 @@
return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior);
}
- C withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+ C withBuilderTransformation(Consumer<B> builderTransformation) {
builderTransformations.add(builderTransformation);
return self();
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index c421c83..73abcee 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -351,6 +351,14 @@
return addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE);
}
+ public T addKeepRuntimeInvisibleAnnotations() {
+ return addKeepAttributes(ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS);
+ }
+
+ public T addKeepRuntimeInvisibleParameterAnnotations() {
+ return addKeepAttributes(ProguardKeepAttributes.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
+ }
+
public T addKeepRuntimeVisibleAnnotations() {
return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
index 385a3fe..3858a1e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
@@ -6,45 +6,72 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.BooleanUtils;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.List;
import org.junit.Test;
+import org.junit.runners.Parameterized;
public class NoClassesOrMembersWithAnnotationsTest extends HorizontalClassMergingTestBase {
- public NoClassesOrMembersWithAnnotationsTest(TestParameters parameters) {
+ private final boolean enableProguardCompatibilityMode;
+
+ @Parameterized.Parameters(name = "{1}, compat: {0}")
+ public static List<Object[]> parameters() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public NoClassesOrMembersWithAnnotationsTest(
+ boolean enableProguardCompatibilityMode, TestParameters parameters) {
super(parameters);
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
}
@Test
public void testR8() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertIsCompleteMergeGroup(A.class, C.class))
+ inspector -> {
+ if (enableProguardCompatibilityMode) {
+ inspector.assertIsCompleteMergeGroup(A.class, C.class);
+ } else {
+ inspector.assertIsCompleteMergeGroup(A.class, B.class, C.class);
+ }
+ })
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(
- "a", "b", "c", "foo", "null", "annotation 2", "annotation 1", "annotation 2")
+ .applyIf(
+ enableProguardCompatibilityMode,
+ result ->
+ result.assertSuccessWithOutputLines(
+ "a", "b", "c", "foo", "null", "annotation 2", "annotation 1", "annotation 2"),
+ result ->
+ result.assertSuccessWithOutputLines("a", "b", "c", "foo", "null", "annotation 2"))
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(TypeAnnotation.class), isPresent());
assertThat(codeInspector.clazz(MethodAnnotation.class), isPresent());
assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(B.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class),
+ onlyIf(enableProguardCompatibilityMode, isPresent()));
assertThat(codeInspector.clazz(C.class), isAbsent());
});
}
@@ -110,14 +137,18 @@
return null;
}
});
- System.out.println(
- b.getClass().getAnnotations()[0].toString().replaceFirst(".*", "annotation 1"));
- System.out.println(
- c.getClass()
- .getMethods()[0]
- .getAnnotations()[0]
- .toString()
- .replaceFirst(".*", "annotation 2"));
+ if (b.getClass().getAnnotations().length > 0) {
+ System.out.println(
+ b.getClass().getAnnotations()[0].toString().replaceFirst(".*", "annotation 1"));
+ }
+ if (c.getClass().getMethods()[0].getAnnotations().length > 0) {
+ System.out.println(
+ c.getClass()
+ .getMethods()[0]
+ .getAnnotations()[0]
+ .toString()
+ .replaceFirst(".*", "annotation 2"));
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
index 795837e..c3a7cfc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.classmerging.vertical;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -41,7 +40,7 @@
@Test
public void testR8() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addInnerClasses(VerticalClassMergingWithNonVisibleAnnotationTestClasses.class)
.addProgramClasses(Sub.class)
.setMinApi(parameters.getApiLevel())
@@ -50,14 +49,14 @@
VerticalClassMergingWithNonVisibleAnnotationTestClasses.class.getTypeName()
+ "$Private* { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(Base.class))
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.run(parameters.getRuntime(), Sub.class)
.assertSuccessWithOutputLines("Base::foo()", "Sub::bar()")
.inspect(
codeInspector -> {
- // Assert that base has been vertically merged into Sub.
- assertThat(codeInspector.clazz(Base.class), not(isPresent()));
ClassSubject sub = codeInspector.clazz(Sub.class);
assertThat(sub, isPresent());
// Assert that the merged class has no annotations from Base
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
index 6382436..c6094ae 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
@@ -42,9 +42,8 @@
Assume.assumeFalse(
"The methods values and valueOf are required for reflection.",
enumKeepRules.toString().equals("none"));
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addInnerClasses(AnnotationEnumUnboxingTest.class)
- .noMinification()
.addKeepMainRule(Main.class)
.addKeepRules(enumKeepRules.getKeepRules())
.addKeepClassRules(ClassAnnotationDefault.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
index 2e3904a..339bbbc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
@@ -36,35 +36,42 @@
@RunWith(Parameterized.class)
public class UninstantiatedAnnotatedArgumentsTest extends TestBase {
+ private final boolean enableProguardCompatibilityMode;
private final boolean keepUninstantiatedArguments;
private final TestParameters parameters;
- @Parameters(name = "{1}, keep uninstantiated arguments: {0}")
+ @Parameters(name = "{2}, compat: {0}, keep uninstantiated arguments: {1}")
public static List<Object[]> params() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
}
public UninstantiatedAnnotatedArgumentsTest(
- boolean keepUninstantiatedArguments, TestParameters parameters) {
+ boolean enableProguardCompatibilityMode,
+ boolean keepUninstantiatedArguments,
+ TestParameters parameters) {
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
this.keepUninstantiatedArguments = keepUninstantiatedArguments;
this.parameters = parameters;
}
@Test
public void test() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
.addInnerClasses(UninstantiatedAnnotatedArgumentsTest.class)
.addConstantArgumentAnnotations()
.addKeepMainRule(TestClass.class)
.addKeepClassRules(Instantiated.class, Uninstantiated.class)
- .addKeepAttributes("RuntimeVisibleParameterAnnotations")
+ .addKeepRuntimeVisibleParameterAnnotations()
.enableNeverClassInliningAnnotations()
.enableConstantArgumentAnnotations(keepUninstantiatedArguments)
.enableInliningAnnotations()
.enableUnusedArgumentAnnotations()
// TODO(b/123060011): Mapping not working in presence of argument removal.
.minification(keepUninstantiatedArguments)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyOutput)
.run(parameters.getRuntime(), TestClass.class)
@@ -94,38 +101,35 @@
assertThat(methodSubject, isPresent());
// TODO(b/131735725): Should also remove arguments from the virtual methods.
- if (keepUninstantiatedArguments || methodSubject.getOriginalName().contains("Virtual")) {
- assertEquals(3, methodSubject.getMethod().getReference().proto.parameters.size());
- assertEquals(3, methodSubject.getMethod().parameterAnnotationsList.size());
+ boolean shouldHaveArgumentRemoval =
+ keepUninstantiatedArguments || methodSubject.getOriginalName().contains("Virtual");
+ if (shouldHaveArgumentRemoval) {
+ assertEquals(3, methodSubject.getMethod().getParameters().size());
- for (int i = 0; i < 3; ++i) {
- DexAnnotationSet annotationSet =
- methodSubject.getMethod().parameterAnnotationsList.get(i);
- assertEquals(1, annotationSet.annotations.length);
-
- DexAnnotation annotation = annotationSet.annotations[0];
- if (i == getPositionOfUnusedArgument(methodSubject)) {
- assertEquals(
- uninstantiatedClassSubject.getFinalName(),
- annotation.annotation.type.toSourceString());
- } else {
- assertEquals(
- instantiatedClassSubject.getFinalName(),
- annotation.annotation.type.toSourceString());
- }
- }
+ // In non-compat mode, R8 removes annotations from non-pinned items.
+ assertEquals(
+ enableProguardCompatibilityMode ? 3 : 0,
+ methodSubject.getMethod().getParameterAnnotations().size());
} else {
assertEquals(2, methodSubject.getMethod().getReference().proto.parameters.size());
- assertEquals(2, methodSubject.getMethod().parameterAnnotationsList.size());
+ assertEquals(
+ enableProguardCompatibilityMode ? 2 : 0,
+ methodSubject.getMethod().getParameterAnnotations().size());
+ }
- for (int i = 0; i < 2; ++i) {
- DexAnnotationSet annotationSet =
- methodSubject.getMethod().parameterAnnotationsList.get(i);
- assertEquals(1, annotationSet.annotations.length);
+ for (int i = 0; i < methodSubject.getMethod().getParameterAnnotations().size(); ++i) {
+ DexAnnotationSet annotationSet = methodSubject.getMethod().getParameterAnnotation(i);
+ assertEquals(1, annotationSet.size());
- DexAnnotation annotation = annotationSet.annotations[0];
+ DexAnnotation annotation = annotationSet.getFirst();
+ if (shouldHaveArgumentRemoval && i == getPositionOfUnusedArgument(methodSubject)) {
assertEquals(
- instantiatedClassSubject.getFinalName(), annotation.annotation.type.toSourceString());
+ uninstantiatedClassSubject.getFinalName(),
+ annotation.getAnnotationType().getTypeName());
+ } else {
+ assertEquals(
+ instantiatedClassSubject.getFinalName(),
+ annotation.getAnnotationType().getTypeName());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java
index 955c5d4..7cf6193 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java
@@ -35,22 +35,30 @@
@RunWith(Parameterized.class)
public class UnusedAnnotatedArgumentsTest extends TestBase {
+ private final boolean enableProguardCompatibilityMode;
private final boolean keepUnusedArguments;
private final TestParameters parameters;
- @Parameters(name = "{1}, keep unused arguments: {0}")
+ @Parameters(name = "{2}, compat: {0}, keep unused arguments: {1}")
public static List<Object[]> params() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public UnusedAnnotatedArgumentsTest(boolean keepUnusedArguments, TestParameters parameters) {
+ public UnusedAnnotatedArgumentsTest(
+ boolean enableProguardCompatibilityMode,
+ boolean keepUnusedArguments,
+ TestParameters parameters) {
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
this.keepUnusedArguments = keepUnusedArguments;
this.parameters = parameters;
}
@Test
public void test() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
.addInnerClasses(UnusedAnnotatedArgumentsTest.class)
.addUnusedArgumentAnnotations()
.addKeepMainRule(TestClass.class)
@@ -61,7 +69,7 @@
.enableUnusedArgumentAnnotations(keepUnusedArguments)
// TODO(b/123060011): Mapping not working in presence of unused argument removal.
.minification(keepUnusedArguments)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyOutput)
.run(parameters.getRuntime(), TestClass.class)
@@ -90,36 +98,24 @@
for (MethodSubject methodSubject : methodSubjects) {
assertThat(methodSubject, isPresent());
- if (keepUnusedArguments) {
- assertEquals(3, methodSubject.getMethod().getReference().proto.parameters.size());
- assertEquals(3, methodSubject.getMethod().parameterAnnotationsList.size());
+ assertEquals(keepUnusedArguments ? 3 : 2, methodSubject.getMethod().getParameters().size());
- for (int i = 0; i < 3; ++i) {
- DexAnnotationSet annotationSet =
- methodSubject.getMethod().parameterAnnotationsList.get(i);
- assertEquals(1, annotationSet.annotations.length);
+ // R8 non-compat removes annotations from non-pinned items.
+ assertEquals(
+ enableProguardCompatibilityMode ? methodSubject.getMethod().getParameters().size() : 0,
+ methodSubject.getMethod().getParameterAnnotations().size());
- DexAnnotation annotation = annotationSet.annotations[0];
- if (i == getPositionOfUnusedArgument(methodSubject)) {
- assertEquals(
- unusedClassSubject.getFinalName(), annotation.annotation.type.toSourceString());
- } else {
- assertEquals(
- usedClassSubject.getFinalName(), annotation.annotation.type.toSourceString());
- }
- }
- } else {
- assertEquals(2, methodSubject.getMethod().getReference().proto.parameters.size());
- assertEquals(2, methodSubject.getMethod().parameterAnnotationsList.size());
+ for (int i = 0; i < methodSubject.getMethod().getParameterAnnotations().size(); ++i) {
+ DexAnnotationSet annotationSet = methodSubject.getMethod().getParameterAnnotation(i);
+ assertEquals(1, annotationSet.size());
- for (int i = 0; i < 2; ++i) {
- DexAnnotationSet annotationSet =
- methodSubject.getMethod().parameterAnnotationsList.get(i);
- assertEquals(1, annotationSet.annotations.length);
-
- DexAnnotation annotation = annotationSet.annotations[0];
+ DexAnnotation annotation = annotationSet.annotations[0];
+ if (keepUnusedArguments && i == getPositionOfUnusedArgument(methodSubject)) {
assertEquals(
- usedClassSubject.getFinalName(), annotation.annotation.type.toSourceString());
+ unusedClassSubject.getFinalName(), annotation.getAnnotationType().toSourceString());
+ } else {
+ assertEquals(
+ usedClassSubject.getFinalName(), annotation.getAnnotationType().toSourceString());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
index 49b90e7..c008972 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
@@ -11,10 +11,11 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,14 +29,18 @@
public class UnusedAnnotatedArgumentsWithMissingAnnotationsTest extends TestBase
implements Opcodes {
+ private final boolean enableProguardCompatibilityMode;
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameterized.Parameters(name = "{1}, compat: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public UnusedAnnotatedArgumentsWithMissingAnnotationsTest(TestParameters parameters) {
+ public UnusedAnnotatedArgumentsWithMissingAnnotationsTest(
+ boolean enableProguardCompatibilityMode, TestParameters parameters) {
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
this.parameters = parameters;
}
@@ -43,26 +48,28 @@
assertThat(clazz, isPresent());
MethodSubject init = clazz.init("Test", "java.lang.String");
assertThat(init, isPresent());
- assertEquals(2, init.getMethod().parameterAnnotationsList.size());
- assertTrue(init.getMethod().parameterAnnotationsList.get(0).isEmpty());
- assertEquals(1, init.getMethod().parameterAnnotationsList.get(1).annotations.length);
- assertEquals(
- "L" + expectedAnnotationClass + ";",
- init.getMethod()
- .parameterAnnotationsList
- .get(1)
- .annotations[0]
- .annotation
- .type
- .descriptor
- .toString());
+ if (enableProguardCompatibilityMode) {
+ assertEquals(2, init.getMethod().getParameterAnnotations().size());
+ assertTrue(init.getMethod().getParameterAnnotation(0).isEmpty());
+ assertEquals(1, init.getMethod().getParameterAnnotation(1).size());
+ assertEquals(
+ "L" + expectedAnnotationClass + ";",
+ init.getMethod()
+ .getParameterAnnotation(1)
+ .annotations[0]
+ .annotation
+ .type
+ .toDescriptorString());
+ } else {
+ assertTrue(init.getMethod().getParameterAnnotations().isEmpty());
+ }
}
@Test
public void test() throws Exception {
String expectedOutput =
StringUtils.lines("In Inner1() used", "In Inner2() used", "In Inner3() used", "In main()");
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
.addProgramClassFileData(
dumpTest(),
dumpInner1(),
diff --git a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
index cc558a1..5ed7de6 100644
--- a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
+++ b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
@@ -171,8 +171,7 @@
.inspector();
}
- private CodeInspector compileR8(Class... classes)
- throws CompilationFailedException, IOException, ExecutionException {
+ private CodeInspector compileR8(Class... classes) throws CompilationFailedException, IOException {
List<String> keepRules =
Arrays.stream(classes)
.map(c -> "-keep class " + c.getCanonicalName() + " { <methods>; }")
@@ -186,11 +185,16 @@
// Keep the input class and its methods.
.addKeepRules(keepRules)
// Keep the annotation class.
- .addKeepRules("-keep class dalvik.annotation.optimization.ReachabilitySensitive")
+ .addKeepRules(
+ "-keep class dalvik.annotation.optimization.ReachabilitySensitive",
+ "-keep,allowshrinking,allowobfuscation class * {",
+ " @dalvik.annotation.optimization.ReachabilitySensitive <fields>;",
+ " @dalvik.annotation.optimization.ReachabilitySensitive <methods>;",
+ "}")
// Keep the annotation so R8 can find it and honor it. It also needs to be available
// at runtime so that the Art runtime can honor it as well, so if it is not kept we
// do not have to honor it as the runtime will not know to do so in any case.
- .addKeepRules("-keepattributes RuntimeVisibleAnnotations")
+ .addKeepRuntimeVisibleAnnotations()
.compile()
.inspector();
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageAnnotationTest.java
index 61526d4..fe19c51 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageAnnotationTest.java
@@ -4,29 +4,61 @@
package com.android.tools.r8.repackage;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class RepackageAnnotationTest extends RepackageTestBase {
- private final String EXPECTED = "Hello World";
+ private static final String EXPECTED = "Hello World";
+ private static final String EXPECTED_WITH_ANNOTATION_REMOVAL = "null";
+
+ private final boolean enableProguardCompatibilityMode;
+ private final boolean keepAllowShrinking;
+
+ @Parameters(name = "{3}, compat: {0}, keep: {1}, kind: {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
public RepackageAnnotationTest(
- String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ boolean enableProguardCompatibilityMode,
+ boolean keepAllowShrinking,
+ String flattenPackageHierarchyOrRepackageClasses,
+ TestParameters parameters) {
super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
+ this.keepAllowShrinking = keepAllowShrinking;
}
@Test
public void testRuntime() throws Exception {
+ assumeFalse(enableProguardCompatibilityMode);
+ assumeFalse(keepAllowShrinking);
+ assumeFalse(isFlattenPackageHierarchy());
testForRuntime(parameters)
.addProgramClasses(Main.class, Annotation.class, A.class)
.run(parameters.getRuntime(), Main.class)
@@ -35,8 +67,17 @@
@Test
public void testR8() throws Exception {
- testForR8(parameters.getBackend())
+ assumeTrue(!enableProguardCompatibilityMode || !keepAllowShrinking);
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
.addProgramClasses(Main.class, A.class, Annotation.class)
+ .applyIf(
+ keepAllowShrinking,
+ builder -> {
+ // Add a keep rule to ensure annotation is retained with R8 non-compat.
+ assertFalse(enableProguardCompatibilityMode);
+ builder.addKeepRules(
+ "-keep,allowshrinking,allowobfuscation class " + A.class.getTypeName());
+ })
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
@@ -47,8 +88,13 @@
" *;",
"}")
.apply(this::configureRepackaging)
+ .compile()
+ .inspect(inspector -> assertThat(Annotation.class, isRepackaged(inspector)))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(EXPECTED);
+ .assertSuccessWithOutputLines(
+ enableProguardCompatibilityMode || keepAllowShrinking
+ ? EXPECTED
+ : EXPECTED_WITH_ANNOTATION_REMOVAL);
}
public static class Main {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java
index 4672079..34cd1af 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java
@@ -30,7 +30,7 @@
@Test
public void test() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.addKeepClassRules(NonPublicKeptAnnotation.class)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java
index 46a83ff..af48e82 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java
@@ -31,7 +31,7 @@
@Test
public void test() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.addKeepClassRules(NonPublicKeptAnnotation.class)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java
index 068cc89..20f8704 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java
@@ -30,7 +30,7 @@
@Test
public void test() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.addKeepClassRules(NonPublicKeptAnnotation.class)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java
index c7cd698..d9833ae 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java
@@ -30,7 +30,7 @@
@Test
public void test() throws Exception {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.addKeepClassRules(NonPublicKeptAnnotation.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainRetentionAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainRetentionAnnotationTest.java
index 597c835..6e25e4ae 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainRetentionAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainRetentionAnnotationTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.shaking.annotations;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeTrue;
@@ -77,14 +79,19 @@
AnnotationSubject targetAnnotationSubject =
annotationClassSubject.annotation(Target.class.getTypeName());
- assertThat(targetAnnotationSubject, isPresent());
+ assertThat(targetAnnotationSubject, onlyIf(shouldOnlyRetainRetention(), isAbsent()));
AnnotationSubject myAnnotationAnnotationSubject =
annotationClassSubject.annotation(MyAnnotation.class.getTypeName());
- assertThat(myAnnotationAnnotationSubject, isPresent());
+ assertThat(
+ myAnnotationAnnotationSubject, onlyIf(shouldOnlyRetainRetention(), isAbsent()));
})
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("3");
+ .assertSuccessWithOutputLines(shouldOnlyRetainRetention() ? "1" : "3");
+ }
+
+ private boolean shouldOnlyRetainRetention() {
+ return !enableProguardCompatibilityMode && !keepAllowShrinking;
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java
index 9c98c6e..e98541c 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java
@@ -59,6 +59,10 @@
.addProgramClasses(Factory.class, MainWithMethodAnnotation.class, C.class)
.addKeepMainRule(MainWithMethodAnnotation.class)
.addKeepClassAndMembersRules(Factory.class)
+ .addKeepRules(
+ "-keepclassmembers,allowobfuscation,allowshrinking class "
+ + MainWithMethodAnnotation.class.getTypeName()
+ + "{void test();}")
.addKeepAttributes("*Annotation*")
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -123,7 +127,7 @@
test();
}
- @Factory(ref = C.class) // <-- We are not explicitly saying that test() should be kept.
+ @Factory(ref = C.class) // <-- We are explicitly saying that test() should be kept.
@NeverInline
public static void test() {
System.out.println("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
index 9246164..d737c41 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
@@ -4,13 +4,16 @@
package com.android.tools.r8.shaking.annotations;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
@@ -18,6 +21,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -32,29 +36,56 @@
"In OtherInterfaceImpl.targetedMethod()",
MyAnnotation.class.getName());
+ private static final String expectedOutputWithAnnotationRemoval =
+ StringUtils.lines(
+ "In InterfaceImpl.targetedMethod()", "In OtherInterfaceImpl.targetedMethod()");
+
+ private final boolean enableProguardCompatibilityMode;
+ private final boolean keepAllowShrinking;
private final TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{2}, compat: {0}, keep: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public AnnotationsOnTargetedMethodTest(TestParameters parameters) {
+ public AnnotationsOnTargetedMethodTest(
+ boolean enableProguardCompatibilityMode,
+ boolean keepAllowShrinking,
+ TestParameters parameters) {
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
+ this.keepAllowShrinking = keepAllowShrinking;
this.parameters = parameters;
}
@Test
public void test() throws Exception {
+ // No need to run R8 compat mode with extra -keep,allowshrinking rule.
+ assumeTrue(!enableProguardCompatibilityMode || !keepAllowShrinking);
if (parameters.isCfRuntime()) {
testForJvm()
.addTestClasspath()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
}
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
.addInnerClasses(AnnotationsOnTargetedMethodTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRuntimeVisibleAnnotations()
+ .applyIf(
+ keepAllowShrinking,
+ builder -> {
+ // Add extra rule to retain the annotation on Interface.targetedMethod() in non-compat
+ // mode.
+ assertFalse(enableProguardCompatibilityMode); // See assumeTrue() above.
+ builder.addKeepRules(
+ "-keepclassmembers,allowshrinking class " + Interface.class.getTypeName() + " {",
+ " void targetedMethod();",
+ "}");
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -62,7 +93,10 @@
.noMinification()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(expectedOutput);
+ .assertSuccessWithOutput(
+ !enableProguardCompatibilityMode && !keepAllowShrinking
+ ? expectedOutputWithAnnotationRemoval
+ : expectedOutput);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java
index 13fbb3e..da0b781 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java
@@ -14,6 +14,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -43,12 +45,21 @@
}
@Test
- public void test() throws Exception {
+ public void testCompat() throws Exception {
+ runTest(true);
+ }
+
+ @Test
+ public void testNonCompat() throws Exception {
+ runTest(false);
+ }
+
+ private void runTest(boolean enableCompatMode) throws Exception {
R8TestRunResult result =
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend(), enableCompatMode)
.addInnerClasses(ProgramAnnotationRemovalTest.class)
.addKeepMainRule(TestClass.class)
- .addKeepAttributes("RuntimeVisibleAnnotations")
+ .addKeepRuntimeVisibleAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -68,14 +79,19 @@
MethodSubject methodWithLiveProgramAnnotationSubject =
testClassSubject.uniqueMethodWithName("methodWithLiveProgramAnnotation");
assertThat(methodWithLiveProgramAnnotationSubject, isPresent());
- assertEquals(1, methodWithLiveProgramAnnotationSubject.getMethod().annotations().size());
+ assertEquals(
+ BooleanUtils.intValue(enableCompatMode),
+ methodWithLiveProgramAnnotationSubject.getMethod().annotations().size());
MethodSubject methodWithDeadProgramAnnotationSubject =
testClassSubject.uniqueMethodWithName("methodWithDeadProgramAnnotation");
assertThat(methodWithDeadProgramAnnotationSubject, isPresent());
assertEquals(0, methodWithDeadProgramAnnotationSubject.getMethod().annotations().size());
- result.assertSuccessWithOutputLines("@" + liveAnnotationClassSubject.getFinalName() + "()");
+ result.applyIf(
+ enableCompatMode,
+ r -> r.assertSuccessWithOutputLines("@" + liveAnnotationClassSubject.getFinalName() + "()"),
+ TestRunResult::assertSuccessWithEmptyOutput);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java
index 16c7d4f..187d79c 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java
@@ -8,21 +8,27 @@
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedAnnotation;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,27 +38,50 @@
@RunWith(Parameterized.class)
public class PrunedOrMergedAnnotationTest extends TestBase {
+ private final boolean enableProguardCompatibilityMode;
+ private final boolean keepForAnnotations;
private final TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{2}, compat: {0}, keep: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public PrunedOrMergedAnnotationTest(TestParameters parameters) {
+ public PrunedOrMergedAnnotationTest(
+ boolean enableProguardCompatibilityMode,
+ boolean keepForAnnotations,
+ TestParameters parameters) {
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
+ this.keepForAnnotations = keepForAnnotations;
this.parameters = parameters;
}
@Test
public void testRewritingInFactory()
throws IOException, CompilationFailedException, ExecutionException {
- testForR8(parameters.getBackend())
+ // No need to add extra keep rules for retaining annotations in compat mode.
+ assumeTrue(!enableProguardCompatibilityMode || !keepForAnnotations);
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
.addInnerClasses(PrunedOrMergedAnnotationTest.class)
.addKeepMainRule(Main.class)
- .addKeepAttributes("*Annotation*")
.addKeepClassAndMembersRules(Factory.class)
+ .addKeepRuntimeInvisibleAnnotations()
+ .addKeepRuntimeInvisibleParameterAnnotations()
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(A.class))
+ .applyIf(
+ keepForAnnotations,
+ builder -> {
+ assertFalse(enableProguardCompatibilityMode);
+ builder.addKeepRules(
+ "-keep,allowshrinking,allowobfuscation class " + C.class.getTypeName());
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello", "World!")
@@ -62,15 +91,21 @@
DexType mergedType = inspector.clazz(B.class).getDexProgramClass().type;
ClassSubject classC = inspector.clazz(C.class);
assertThat(classC, isPresent());
- DexEncodedAnnotation annotation =
- classC.annotation(Factory.class.getTypeName()).getAnnotation();
- assertTrue(valueIsDexType(mergedType, annotation.elements[0].value));
- assertTrue(
- Arrays.stream(annotation.elements[1].value.asDexValueArray().getValues())
- .allMatch(value -> valueIsDexType(mergedType, value)));
- // Check that method parameter annotations are rewritten as well.
- DexEncodedMethod method = inspector.clazz(Main.class).mainMethod().getMethod();
- DexAnnotationSet annotationSet = method.parameterAnnotationsList.get(0);
+
+ MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod();
+ if (enableProguardCompatibilityMode || keepForAnnotations) {
+ DexEncodedAnnotation annotation =
+ classC.annotation(Factory.class.getTypeName()).getAnnotation();
+ assertTrue(valueIsDexType(mergedType, annotation.elements[0].value));
+ assertTrue(
+ Arrays.stream(annotation.elements[1].value.asDexValueArray().getValues())
+ .allMatch(value -> valueIsDexType(mergedType, value)));
+ } else {
+ assertTrue(classC.getDexProgramClass().annotations().isEmpty());
+ }
+
+ // Check that method parameter annotations are rewritten.
+ DexAnnotationSet annotationSet = mainMethod.getMethod().getParameterAnnotation(0);
DexEncodedAnnotation parameterAnnotation = annotationSet.annotations[0].annotation;
assertTrue(valueIsDexType(mergedType, parameterAnnotation.elements[0].value));
});
@@ -82,6 +117,7 @@
return true;
}
+ @Retention(RetentionPolicy.CLASS)
public @interface Factory {
Class<?> extending() default Object.class;
@@ -92,6 +128,7 @@
public static class A {}
@NeverClassInline
+ @NoHorizontalClassMerging
public static class B extends A {
@NeverInline
public void world() {
@@ -102,6 +139,7 @@
@Factory(
extending = A.class,
other = {A.class, B.class})
+ @NoHorizontalClassMerging
public static class C {
@NeverInline
public static void hello() {
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index f102535..662ad5e 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -95,7 +95,7 @@
@Test
public void b120951621_keepAll() throws Exception {
CodeInspector inspector =
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addProgramFiles(
compiledJars.getForConfiguration(kotlinc, targetVersion),
getKotlinAnnotationJar(kotlinc))
@@ -133,7 +133,7 @@
@Test
public void b120951621_partiallyKeep() throws Exception {
CodeInspector inspector =
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addProgramFiles(
compiledJars.getForConfiguration(kotlinc, targetVersion),
getKotlinAnnotationJar(kotlinc))
@@ -175,7 +175,7 @@
@Test
public void b120951621_keepAnnotation() throws Exception {
CodeInspector inspector =
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addProgramFiles(
compiledJars.getForConfiguration(kotlinc, targetVersion),
getKotlinAnnotationJar(kotlinc))
diff --git a/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java
index 61a9845..50f1954 100644
--- a/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java
@@ -50,6 +50,7 @@
"-keepclassmembernames class " + Enum.class.getTypeName() + " { <fields>; }"))
.setMinApi(parameters.getApiLevel())
.addKeepRuntimeVisibleAnnotations()
+ .compile()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("TEST_ONE");
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 9933b84..e00615a 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -6,7 +6,9 @@
import static com.android.tools.r8.references.Reference.classFromClass;
import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -122,7 +124,6 @@
mainClass, MentionedClass.class, mentionedClassWithAnnotations, annotationClass)
.addKeepMainRule(mainClass)
.allowAccessModification()
- .noMinification()
.addKeepClassAndMembersRules(annotationClass)
.map(b -> keepAnnotations ? b.addKeepAttributes("*Annotation*") : b)
.setMinApi(parameters.getApiLevel())
@@ -134,10 +135,15 @@
assertThat(clazz, isPresent());
// The test contains only a member class so the enclosing-method attribute will be null.
- assertEquals(
- forceProguardCompatibility, !clazz.getDexProgramClass().getInnerClasses().isEmpty());
- assertEquals(forceProguardCompatibility || keepAnnotations,
- clazz.annotation(annotationClass.getCanonicalName()).isPresent());
+ assertTrue(clazz.getDexProgramClass().getInnerClasses().isEmpty());
+
+ if (keepAnnotations) {
+ assertThat(
+ clazz.annotation(annotationClass.getCanonicalName()),
+ onlyIf(forceProguardCompatibility, isPresent()));
+ } else {
+ assertThat(clazz.annotation(annotationClass.getCanonicalName()), isAbsent());
+ }
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
index 880ed52..edbcd98 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
@@ -54,8 +54,11 @@
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
verifyClassesAbsent(codeInspector,
UnusedAnnotation.class, UnusedAnnotationDependent.class);
- verifyClassesPresent(codeInspector,
- UsedAnnotation.class, UsedAnnotationDependent.class);
+ if (shrinker.isFullModeR8()) {
+ verifyClassesAbsent(codeInspector, UsedAnnotation.class, UsedAnnotationDependent.class);
+ } else {
+ verifyClassesPresent(codeInspector, UsedAnnotation.class, UsedAnnotationDependent.class);
+ }
}
@Test
@@ -80,8 +83,11 @@
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
verifyClassesAbsent(codeInspector,
UnusedAnnotation.class, UnusedAnnotationDependent.class);
- verifyClassesPresent(codeInspector,
- UsedAnnotation.class, UsedAnnotationDependent.class);
+ if (shrinker.isFullModeR8()) {
+ verifyClassesAbsent(codeInspector, UsedAnnotation.class, UsedAnnotationDependent.class);
+ } else {
+ verifyClassesPresent(codeInspector, UsedAnnotation.class, UsedAnnotationDependent.class);
+ }
}
@Test
@@ -104,8 +110,11 @@
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
verifyClassesAbsent(codeInspector,
UnusedAnnotation.class, UnusedAnnotationDependent.class);
- verifyClassesPresent(codeInspector,
- UsedAnnotation.class, UsedAnnotationDependent.class);
+ if (shrinker.isFullModeR8()) {
+ verifyClassesAbsent(codeInspector, UsedAnnotation.class, UsedAnnotationDependent.class);
+ } else {
+ verifyClassesPresent(codeInspector, UsedAnnotation.class, UsedAnnotationDependent.class);
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java b/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
index d578ae2..b97fd41 100644
--- a/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
+++ b/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
@@ -6,12 +6,29 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import java.util.Map;
import java.util.function.Function;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
-public class ArrayUtilsTest {
+@RunWith(Parameterized.class)
+public class ArrayUtilsTest extends TestBase {
+
+ private static final Integer[] EMPTY_INTEGER_ARRAY = new Integer[0];
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public ArrayUtilsTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
private Integer[] createInputData(int size) {
Integer[] input = new Integer[size];
@@ -124,7 +141,7 @@
public void testMap_identity() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.map(Integer[].class, input, Function.identity());
+ Integer[] output = ArrayUtils.map(input, Function.identity(), EMPTY_INTEGER_ARRAY);
assertEquals(input, output);
}
@@ -132,7 +149,7 @@
public void testMap_dropOdd() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.map(Integer[].class, input, x -> x % 2 != 0 ? null : x);
+ Integer[] output = ArrayUtils.map(input, x -> x % 2 != 0 ? null : x, EMPTY_INTEGER_ARRAY);
assertNotEquals(input, output);
assertEquals(2, output.length);
assertEquals(0, (int) output[0]);
@@ -143,7 +160,7 @@
public void testMap_dropAll() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.map(Integer[].class, input, x -> null);
+ Integer[] output = ArrayUtils.map(input, x -> null, EMPTY_INTEGER_ARRAY);
assertNotEquals(input, output);
assertEquals(0, output.length);
}
@@ -152,7 +169,7 @@
public void testMap_double() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.map(Integer[].class, input, x -> 2 * x);
+ Integer[] output = ArrayUtils.map(input, x -> 2 * x, EMPTY_INTEGER_ARRAY);
assertNotEquals(input, output);
assertEquals(size, output.length);
for (int i = 0; i < size; i++) {
@@ -164,7 +181,7 @@
public void testMap_double_onlyOdd() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.map(Integer[].class, input, x -> x % 2 != 0 ? 2 * x : x);
+ Integer[] output = ArrayUtils.map(input, x -> x % 2 != 0 ? 2 * x : x, EMPTY_INTEGER_ARRAY);
assertNotEquals(input, output);
assertEquals(size, output.length);
for (int i = 0; i < size; i++) {