Version 2.0.40
Cherry pick: Fix test for keeping annotated items
CL: https://r8-review.googlesource.com/c/r8/+/49024
Cherry pick: Add a test for keeping annotated items
CL: https://r8-review.googlesource.com/c/r8/+/48964
Cherry pick: Unify ways to relax assertion/warning for inner class name.
CL: https://r8-review.googlesource.com/c/r8/+/49060
Bug: 149729626
Change-Id: I87169164acde50b43567a6e2e8e0d5cae60885a2
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index dbe1572..06fb565 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -313,7 +313,10 @@
options.getProguardConfiguration().getRules(), synthesizedProguardRules))
.run(executorService));
- AppView<AppInfoWithLiveness> appViewWithLiveness = runEnqueuer(executorService, appView);
+ AnnotationRemover.Builder annotationRemoverBuilder =
+ options.isShrinking() ? AnnotationRemover.builder() : null;
+ AppView<AppInfoWithLiveness> appViewWithLiveness =
+ runEnqueuer(annotationRemoverBuilder, executorService, appView);
assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness.appInfo());
@@ -352,14 +355,15 @@
pruner.getMethodsToKeepForConfigurationDebugging()));
appView.setAppServices(appView.appServices().prunedCopy(pruner.getRemovedClasses()));
new AbstractMethodRemover(appView.appInfo().withLiveness()).run();
+
+ AnnotationRemover annotationRemover =
+ annotationRemoverBuilder
+ .computeClassesToRetainInnerClassAttributeFor(appViewWithLiveness)
+ .build(appViewWithLiveness);
+ annotationRemover.ensureValid().run();
+ classesToRetainInnerClassAttributeFor =
+ annotationRemover.getClassesToRetainInnerClassAttributeFor();
}
-
- classesToRetainInnerClassAttributeFor =
- AnnotationRemover.computeClassesToRetainInnerClassAttributeFor(appView.withLiveness());
- new AnnotationRemover(appView.withLiveness(), classesToRetainInnerClassAttributeFor)
- .ensureValid()
- .run();
-
} finally {
timing.end();
}
@@ -658,7 +662,9 @@
// Remove annotations that refer to types that no longer exist.
assert classesToRetainInnerClassAttributeFor != null;
- new AnnotationRemover(appView.withLiveness(), classesToRetainInnerClassAttributeFor)
+ AnnotationRemover.builder()
+ .setClassesToRetainInnerClassAttributeFor(classesToRetainInnerClassAttributeFor)
+ .build(appView.withLiveness())
.run();
if (!mainDexClasses.isEmpty()) {
// Remove types that no longer exists from the computed main dex list.
@@ -800,10 +806,13 @@
}
}
- private AppView<AppInfoWithLiveness> runEnqueuer(ExecutorService executorService,
- AppView<AppInfoWithSubtyping> appView) throws ExecutionException {
+ private AppView<AppInfoWithLiveness> runEnqueuer(
+ AnnotationRemover.Builder annotationRemoverBuilder,
+ ExecutorService executorService,
+ AppView<AppInfoWithSubtyping> appView)
+ throws ExecutionException {
Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView);
-
+ enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder);
if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 53d04aa..1c15adc 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "2.0.39";
+ public static final String LABEL = "2.0.40";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 7d63446..4040acd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.AnnotationRemover;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.PredicateUtils;
@@ -401,6 +403,11 @@
setVirtualMethods(newVirtualMethods);
}
+ public DexAnnotationSet liveAnnotations(AppView<AppInfoWithLiveness> appView) {
+ return annotations.keepIf(
+ annotation -> AnnotationRemover.shouldKeepAnnotation(appView, this, annotation));
+ }
+
/**
* For all annotations on the class and all annotations on its methods and fields apply the
* specified consumer.
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 41506ec..dd4e2c5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -62,6 +62,8 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.AnnotationRemover;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
@@ -256,6 +258,16 @@
assert parameterAnnotationsList != null;
}
+ public DexAnnotationSet liveAnnotations(AppView<AppInfoWithLiveness> appView) {
+ return annotations.keepIf(
+ annotation -> AnnotationRemover.shouldKeepAnnotation(appView, this, annotation));
+ }
+
+ public ParameterAnnotationsList liveParameterAnnotations(AppView<AppInfoWithLiveness> appView) {
+ return parameterAnnotationsList.keepIf(
+ annotation -> AnnotationRemover.shouldKeepAnnotation(appView, this, annotation));
+ }
+
public OptionalBool isLibraryMethodOverride() {
return isNonPrivateVirtualMethod() ? isLibraryMethodOverride : OptionalBool.FALSE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index a5e9d5c4..1450e78 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -241,8 +241,7 @@
.forEachOrdered(
lambda -> {
try {
- LambdaGroupId id =
- KotlinLambdaGroupIdFactory.create(kotlin, lambda, appView.options());
+ LambdaGroupId id = KotlinLambdaGroupIdFactory.create(appView, kotlin, lambda);
LambdaGroup group = groups.computeIfAbsent(id, LambdaGroupId::createGroup);
group.add(lambda);
lambdas.put(lambda.type, group);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index 07c1b08..df6f812 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -21,6 +22,7 @@
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
import com.android.tools.r8.kotlin.Kotlin;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
import java.util.List;
@@ -123,10 +125,16 @@
// Specialized group id.
final static class GroupId extends KotlinLambdaGroupId {
- GroupId(String capture, DexType iface,
- String pkg, String signature, DexEncodedMethod mainMethod,
- InnerClassAttribute inner, EnclosingMethodAttribute enclosing) {
- super(capture, iface, pkg, signature, mainMethod, inner, enclosing);
+ GroupId(
+ AppView<AppInfoWithLiveness> appView,
+ String capture,
+ DexType iface,
+ String pkg,
+ String signature,
+ DexEncodedMethod mainMethod,
+ InnerClassAttribute inner,
+ EnclosingMethodAttribute enclosing) {
+ super(appView, capture, iface, pkg, signature, mainMethod, inner, enclosing);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
index 6957cb1..8f26df4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
@@ -11,15 +12,17 @@
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
final class JStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
static final JStyleLambdaGroupIdFactory INSTANCE = new JStyleLambdaGroupIdFactory();
@Override
- LambdaGroupId validateAndCreate(Kotlin kotlin, DexClass lambda, InternalOptions options)
+ LambdaGroupId validateAndCreate(
+ AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError {
- boolean accessRelaxed = options.getProguardConfiguration().isAccessModificationAllowed();
+ boolean accessRelaxed =
+ appView.options().getProguardConfiguration().isAccessModificationAllowed();
assert lambda.hasKotlinInfo() && lambda.getKotlinInfo().isSyntheticClass();
assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
@@ -35,12 +38,18 @@
String captureSignature = validateInstanceFields(lambda, accessRelaxed);
validateDirectMethods(lambda);
DexEncodedMethod mainMethod = validateVirtualMethods(lambda);
- String genericSignature = validateAnnotations(kotlin, lambda);
+ String genericSignature = validateAnnotations(appView, kotlin, lambda);
InnerClassAttribute innerClass = validateInnerClasses(lambda);
- return new JStyleLambdaGroup.GroupId(captureSignature, iface,
+ return new JStyleLambdaGroup.GroupId(
+ appView,
+ captureSignature,
+ iface,
accessRelaxed ? "" : lambda.type.getPackageDescriptor(),
- genericSignature, mainMethod, innerClass, lambda.getEnclosingMethod());
+ genericSignature,
+ mainMethod,
+ innerClass,
+ lambda.getEnclosingMethod());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index 9e07c44..9f9e767 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -24,6 +25,7 @@
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
import com.android.tools.r8.kotlin.Kotlin;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
import java.util.List;
@@ -129,10 +131,16 @@
// Specialized group id.
final static class GroupId extends KotlinLambdaGroupId {
- GroupId(String capture, DexType iface,
- String pkg, String signature, DexEncodedMethod mainMethod,
- InnerClassAttribute inner, EnclosingMethodAttribute enclosing) {
- super(capture, iface, pkg, signature, mainMethod, inner, enclosing);
+ GroupId(
+ AppView<AppInfoWithLiveness> appView,
+ String capture,
+ DexType iface,
+ String pkg,
+ String signature,
+ DexEncodedMethod mainMethod,
+ InnerClassAttribute inner,
+ EnclosingMethodAttribute enclosing) {
+ super(appView, capture, iface, pkg, signature, mainMethod, inner, enclosing);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
index 3d60e0f..eaf154c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
@@ -11,15 +12,17 @@
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
final class KStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
static final KotlinLambdaGroupIdFactory INSTANCE = new KStyleLambdaGroupIdFactory();
@Override
- LambdaGroupId validateAndCreate(Kotlin kotlin, DexClass lambda, InternalOptions options)
+ LambdaGroupId validateAndCreate(
+ AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError {
- boolean accessRelaxed = options.getProguardConfiguration().isAccessModificationAllowed();
+ boolean accessRelaxed =
+ appView.options().getProguardConfiguration().isAccessModificationAllowed();
assert lambda.hasKotlinInfo() && lambda.getKotlinInfo().isSyntheticClass();
assert lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda();
@@ -35,12 +38,18 @@
String captureSignature = validateInstanceFields(lambda, accessRelaxed);
validateDirectMethods(lambda);
DexEncodedMethod mainMethod = validateVirtualMethods(lambda);
- String genericSignature = validateAnnotations(kotlin, lambda);
+ String genericSignature = validateAnnotations(appView, kotlin, lambda);
InnerClassAttribute innerClass = validateInnerClasses(lambda);
- return new KStyleLambdaGroup.GroupId(captureSignature, iface,
+ return new KStyleLambdaGroup.GroupId(
+ appView,
+ captureSignature,
+ iface,
accessRelaxed ? "" : lambda.type.getPackageDescriptor(),
- genericSignature, mainMethod, innerClass, lambda.getEnclosingMethod());
+ genericSignature,
+ mainMethod,
+ innerClass,
+ lambda.getEnclosingMethod());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
index 996099b..7f24d79 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProto;
@@ -14,6 +15,7 @@
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
abstract class KotlinLambdaGroupId implements LambdaGroupId {
private static final int MISSING_INNER_CLASS_ATTRIBUTE = -1;
@@ -50,8 +52,15 @@
// access from InnerClassAttribute.
final int innerClassAccess;
- KotlinLambdaGroupId(String capture, DexType iface, String pkg, String signature,
- DexEncodedMethod mainMethod, InnerClassAttribute inner, EnclosingMethodAttribute enclosing) {
+ KotlinLambdaGroupId(
+ AppView<AppInfoWithLiveness> appView,
+ String capture,
+ DexType iface,
+ String pkg,
+ String signature,
+ DexEncodedMethod mainMethod,
+ InnerClassAttribute inner,
+ EnclosingMethodAttribute enclosing) {
assert capture != null && iface != null && pkg != null && mainMethod != null;
assert inner == null || (inner.isAnonymous() && inner.getOuter() == null);
this.capture = capture;
@@ -60,8 +69,8 @@
this.signature = signature;
this.mainMethodName = mainMethod.method.name;
this.mainMethodProto = mainMethod.method.proto;
- this.mainMethodAnnotations = mainMethod.annotations;
- this.mainMethodParamAnnotations = mainMethod.parameterAnnotationsList;
+ this.mainMethodAnnotations = mainMethod.liveAnnotations(appView);
+ this.mainMethodParamAnnotations = mainMethod.liveParameterAnnotations(appView);
this.innerClassAccess = inner != null ? inner.getAccess() : MISSING_INNER_CLASS_ATTRIBUTE;
this.enclosing = enclosing;
this.hash = computeHashCode();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 568a8cb..18b4f00 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -15,7 +16,7 @@
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
public abstract class KotlinLambdaGroupIdFactory implements KotlinLambdaConstants {
@@ -29,19 +30,21 @@
// At this point we only perform high-level checks before qualifying the lambda as a candidate
// for merging and assigning lambda group id. We can NOT perform checks on method bodies since
// they may not be converted yet, we'll do that in KStyleLambdaClassValidator.
- public static LambdaGroupId create(Kotlin kotlin, DexClass lambda, InternalOptions options)
+ public static LambdaGroupId create(
+ AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError {
assert lambda.hasKotlinInfo() && lambda.getKotlinInfo().isSyntheticClass();
if (lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
- return KStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(kotlin, lambda, options);
+ return KStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(appView, kotlin, lambda);
}
assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
- return JStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(kotlin, lambda, options);
+ return JStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(appView, kotlin, lambda);
}
- abstract LambdaGroupId validateAndCreate(Kotlin kotlin, DexClass lambda, InternalOptions options)
+ abstract LambdaGroupId validateAndCreate(
+ AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError;
abstract void validateSuperclass(Kotlin kotlin, DexClass lambda) throws LambdaStructureError;
@@ -101,26 +104,25 @@
return true;
}
- String validateAnnotations(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
+ String validateAnnotations(AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
+ throws LambdaStructureError {
String signature = null;
- if (!lambda.annotations.isEmpty()) {
- for (DexAnnotation annotation : lambda.annotations.annotations) {
- if (DexAnnotation.isSignatureAnnotation(annotation, kotlin.factory)) {
- signature = DexAnnotation.getSignature(annotation);
- continue;
- }
-
- if (annotation.annotation.type == kotlin.metadata.kotlinMetadataType) {
- // Ignore kotlin metadata on lambda classes. Metadata on synthetic
- // classes exists but is not used in the current Kotlin version (1.2.21)
- // and newly generated lambda _group_ class is not exactly a kotlin class.
- continue;
- }
-
- assert !hasValidAnnotations(kotlin, lambda);
- throw new LambdaStructureError(
- "unexpected annotation: " + annotation.annotation.type.toSourceString());
+ for (DexAnnotation annotation : lambda.liveAnnotations(appView).annotations) {
+ if (DexAnnotation.isSignatureAnnotation(annotation, kotlin.factory)) {
+ signature = DexAnnotation.getSignature(annotation);
+ continue;
}
+
+ if (annotation.annotation.type == kotlin.metadata.kotlinMetadataType) {
+ // Ignore kotlin metadata on lambda classes. Metadata on synthetic
+ // classes exists but is not used in the current Kotlin version (1.2.21)
+ // and newly generated lambda _group_ class is not exactly a kotlin class.
+ continue;
+ }
+
+ assert !hasValidAnnotations(kotlin, lambda);
+ throw new LambdaStructureError(
+ "unexpected annotation: " + annotation.annotation.type.toSourceString());
}
assert hasValidAnnotations(kotlin, lambda);
return signature;
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index d13cc3b..6e04d4a 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -69,9 +69,7 @@
this.packageObfuscationMode = options.getProguardConfiguration().getPackageObfuscationMode();
this.isAccessModificationAllowed =
options.getProguardConfiguration().isAccessModificationAllowed();
- this.keepInnerClassStructure =
- options.getProguardConfiguration().getKeepAttributes().signature
- || options.getProguardConfiguration().getKeepAttributes().innerClasses;
+ this.keepInnerClassStructure = options.keepInnerClassStructure();
// Initialize top-level naming state.
topLevelState = new Namespace(
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index 8ea3d93..0c85641 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -83,7 +83,8 @@
}
int index = innerTypeMapped.lastIndexOf(separator);
if (index < 0) {
- assert options.getProguardConfiguration().hasApplyMappingFile()
+ assert !options.keepInnerClassStructure()
+ || options.getProguardConfiguration().hasApplyMappingFile()
: innerType + " -> " + innerTypeMapped;
String descriptor = lookupDescriptor(innerType).toString();
return options.itemFactory.createString(
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index 45c59a8..7747eda 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -258,7 +258,7 @@
int innerClassPos = enclosingRenamedBinaryName.length() + 1;
if (innerClassPos < fullRenamedBinaryName.length()) {
renamedSignature.append(fullRenamedBinaryName.substring(innerClassPos));
- } else {
+ } else if (appView.options().keepInnerClassStructure()) {
reporter.warning(
new StringDiagnostic(
"Should have retained InnerClasses attribute of " + type + ".",
@@ -267,7 +267,7 @@
}
} else {
// Did not find the class - keep the inner class name as is.
- // TODO(110085899): Warn about missing classes in signatures?
+ // TODO(b/110085899): Warn about missing classes in signatures?
renamedSignature.append(name);
}
return type;
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationMatchResult.java b/src/main/java/com/android/tools/r8/shaking/AnnotationMatchResult.java
new file mode 100644
index 0000000..f5a3bbf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationMatchResult.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAnnotation;
+
+public abstract class AnnotationMatchResult {
+
+ public boolean isConcreteAnnotationMatchResult() {
+ return false;
+ }
+
+ public ConcreteAnnotationMatchResult asConcreteAnnotationMatchResult() {
+ return null;
+ }
+
+ static class AnnotationsIgnoredMatchResult extends AnnotationMatchResult {
+
+ private static final AnnotationsIgnoredMatchResult INSTANCE =
+ new AnnotationsIgnoredMatchResult();
+
+ private AnnotationsIgnoredMatchResult() {}
+
+ public static AnnotationsIgnoredMatchResult getInstance() {
+ return INSTANCE;
+ }
+ }
+
+ static class ConcreteAnnotationMatchResult extends AnnotationMatchResult {
+
+ private final DexAnnotation matchedAnnotation;
+
+ public ConcreteAnnotationMatchResult(DexAnnotation matchedAnnotation) {
+ this.matchedAnnotation = matchedAnnotation;
+ }
+
+ public DexAnnotation getMatchedAnnotation() {
+ return matchedAnnotation;
+ }
+
+ @Override
+ public boolean isConcreteAnnotationMatchResult() {
+ return true;
+ }
+
+ @Override
+ public ConcreteAnnotationMatchResult asConcreteAnnotationMatchResult() {
+ return this;
+ }
+ }
+}
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 fd3e7f5..4136d1a 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -20,8 +20,8 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -29,26 +29,50 @@
public class AnnotationRemover {
private final AppView<AppInfoWithLiveness> appView;
- private final ProguardKeepAttributes keep;
+ private final Set<DexAnnotation> annotationsToRetain;
private final Set<DexType> classesToRetainInnerClassAttributeFor;
+ private final ProguardKeepAttributes keep;
public AnnotationRemover(
AppView<AppInfoWithLiveness> appView, Set<DexType> classesToRetainInnerClassAttributeFor) {
+ this(appView, classesToRetainInnerClassAttributeFor, ImmutableSet.of());
+ }
+
+ private AnnotationRemover(
+ AppView<AppInfoWithLiveness> appView,
+ Set<DexType> classesToRetainInnerClassAttributeFor,
+ Set<DexAnnotation> annotationsToRetain) {
this.appView = appView;
- this.keep = appView.options().getProguardConfiguration().getKeepAttributes();
+ this.annotationsToRetain = annotationsToRetain;
this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor;
+ this.keep = appView.options().getProguardConfiguration().getKeepAttributes();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public Set<DexType> getClassesToRetainInnerClassAttributeFor() {
+ return classesToRetainInnerClassAttributeFor;
}
/** Used to filter annotations on classes, methods and fields. */
private boolean filterAnnotations(DexDefinition holder, DexAnnotation annotation) {
- return shouldKeepAnnotation(holder, annotation, isAnnotationTypeLive(annotation), appView);
+ return annotationsToRetain.contains(annotation)
+ || shouldKeepAnnotation(appView, holder, annotation, isAnnotationTypeLive(annotation));
}
- static boolean shouldKeepAnnotation(
+ public static boolean shouldKeepAnnotation(
+ AppView<AppInfoWithLiveness> appView, DexDefinition holder, DexAnnotation annotation) {
+ return shouldKeepAnnotation(
+ appView, holder, annotation, isAnnotationTypeLive(annotation, appView));
+ }
+
+ public static boolean shouldKeepAnnotation(
+ AppView<?> appView,
DexDefinition holder,
DexAnnotation annotation,
- boolean isAnnotationTypeLive,
- AppView<?> appView) {
+ boolean isAnnotationTypeLive) {
ProguardKeepAttributes config =
appView.options().getProguardConfiguration() != null
? appView.options().getProguardConfiguration().getKeepAttributes()
@@ -108,6 +132,11 @@
}
private boolean isAnnotationTypeLive(DexAnnotation annotation) {
+ return isAnnotationTypeLive(annotation, appView);
+ }
+
+ private static boolean isAnnotationTypeLive(
+ DexAnnotation annotation, AppView<AppInfoWithLiveness> appView) {
DexType annotationType = annotation.annotation.type.toBaseType(appView.dexItemFactory());
return appView.appInfo().isNonProgramTypeOrLiveProgramType(annotationType);
}
@@ -116,6 +145,9 @@
* 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;
@@ -165,68 +197,6 @@
return false;
}
- public static Set<DexType> computeClassesToRetainInnerClassAttributeFor(
- AppView<? extends AppInfoWithLiveness> appView) {
- // In case of minification for certain inner classes we need to retain their InnerClass
- // attributes because their minified name still needs to be in hierarchical format
- // (enclosing$inner) otherwise the GenericSignatureRewriter can't produce the correct,
- // renamed signature.
-
- // More precisely:
- // - we're going to retain the InnerClass attribute that refers to the same class as 'inner'
- // - for live, inner, nonstatic classes
- // - that are enclosed by a class with a generic signature.
-
- // In compat mode we always keep all InnerClass attributes (if requested).
- // If not requested we never keep any. In these cases don't compute eligible classes.
- if (appView.options().forceProguardCompatibility
- || !appView.options().getProguardConfiguration().getKeepAttributes().innerClasses) {
- return Collections.emptySet();
- }
-
- // Build lookup table and set of the interesting classes.
- // enclosingClasses.get(clazz) gives the enclosing class of 'clazz'
- Map<DexType, DexProgramClass> enclosingClasses = new IdentityHashMap<>();
- Set<DexProgramClass> genericClasses = Sets.newIdentityHashSet();
-
- Iterable<DexProgramClass> programClasses = appView.appInfo().classes();
- for (DexProgramClass clazz : programClasses) {
- if (hasSignatureAnnotation(clazz, appView.dexItemFactory())) {
- genericClasses.add(clazz);
- }
- for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
- if ((innerClassAttribute.getAccess() & Constants.ACC_STATIC) == 0
- && innerClassAttribute.getOuter() == clazz.type) {
- enclosingClasses.put(innerClassAttribute.getInner(), clazz);
- }
- }
- }
-
- Set<DexType> result = Sets.newIdentityHashSet();
- for (DexProgramClass clazz : programClasses) {
- // 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.
- if (appView.appInfo().isPinned(clazz.type)) {
- for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
- DexType inner = innerClassAttribute.getInner();
- if (appView.appInfo().isNonProgramTypeOrLiveProgramType(inner)) {
- result.add(inner);
- }
- DexType context = innerClassAttribute.getLiveContext(appView.appInfo());
- if (context != null && appView.appInfo().isNonProgramTypeOrLiveProgramType(context)) {
- result.add(context);
- }
- }
- }
- if (clazz.getInnerClassAttributeForThisClass() != null
- && appView.appInfo().isNonProgramTypeOrLiveProgramType(clazz.type)
- && hasGenericEnclosingClass(clazz, enclosingClasses, genericClasses)) {
- result.add(clazz.type);
- }
- }
- return result;
- }
-
public void run() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
stripAttributes(clazz);
@@ -251,11 +221,11 @@
private DexAnnotation rewriteAnnotation(DexDefinition holder, DexAnnotation original) {
// Check if we should keep this annotation first.
- if (!filterAnnotations(holder, original)) {
- return null;
+ if (filterAnnotations(holder, original)) {
+ // Then, filter out values that refer to dead definitions.
+ return original.rewrite(this::rewriteEncodedAnnotation);
}
- // Then, filter out values that refer to dead definitions.
- return original.rewrite(this::rewriteEncodedAnnotation);
+ return null;
}
private DexEncodedAnnotation rewriteEncodedAnnotation(DexEncodedAnnotation original) {
@@ -368,4 +338,91 @@
}
}
}
+
+ public static class Builder {
+
+ /**
+ * The set of annotations that were matched by a conditional if rule. These are needed for the
+ * interpretation of if rules in the second round of tree shaking.
+ */
+ private final Set<DexAnnotation> annotationsToRetain = Sets.newIdentityHashSet();
+
+ private Set<DexType> classesToRetainInnerClassAttributeFor;
+
+ public Builder computeClassesToRetainInnerClassAttributeFor(
+ AppView<AppInfoWithLiveness> appView) {
+ assert classesToRetainInnerClassAttributeFor == null;
+ // In case of minification for certain inner classes we need to retain their InnerClass
+ // attributes because their minified name still needs to be in hierarchical format
+ // (enclosing$inner) otherwise the GenericSignatureRewriter can't produce the correct,
+ // renamed signature.
+
+ // More precisely:
+ // - we're going to retain the InnerClass attribute that refers to the same class as 'inner'
+ // - for live, inner, nonstatic classes
+ // - that are enclosed by a class with a generic signature.
+
+ // In compat mode we always keep all InnerClass attributes (if requested).
+ // If not requested we never keep any. In these cases don't compute eligible classes.
+ Set<DexType> result = Sets.newIdentityHashSet();
+ if (!appView.options().forceProguardCompatibility
+ && appView.options().getProguardConfiguration().getKeepAttributes().innerClasses) {
+ // Build lookup table and set of the interesting classes.
+ // enclosingClasses.get(clazz) gives the enclosing class of 'clazz'
+ Map<DexType, DexProgramClass> enclosingClasses = new IdentityHashMap<>();
+ Set<DexProgramClass> genericClasses = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (hasSignatureAnnotation(clazz, appView.dexItemFactory())) {
+ genericClasses.add(clazz);
+ }
+ for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+ if ((innerClassAttribute.getAccess() & Constants.ACC_STATIC) == 0
+ && innerClassAttribute.getOuter() == clazz.type) {
+ enclosingClasses.put(innerClassAttribute.getInner(), clazz);
+ }
+ }
+ }
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ // 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.
+ if (appView.appInfo().isPinned(clazz.type)) {
+ for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+ DexType inner = innerClassAttribute.getInner();
+ if (appView.appInfo().isNonProgramTypeOrLiveProgramType(inner)) {
+ result.add(inner);
+ }
+ DexType context = innerClassAttribute.getLiveContext(appView.appInfo());
+ if (context != null && appView.appInfo().isNonProgramTypeOrLiveProgramType(context)) {
+ result.add(context);
+ }
+ }
+ }
+ if (clazz.getInnerClassAttributeForThisClass() != null
+ && appView.appInfo().isNonProgramTypeOrLiveProgramType(clazz.type)
+ && hasGenericEnclosingClass(clazz, enclosingClasses, genericClasses)) {
+ result.add(clazz.type);
+ }
+ }
+ }
+ classesToRetainInnerClassAttributeFor = result;
+ return this;
+ }
+
+ public Builder setClassesToRetainInnerClassAttributeFor(
+ Set<DexType> classesToRetainInnerClassAttributeFor) {
+ this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor;
+ return this;
+ }
+
+ public void retainAnnotation(DexAnnotation annotation) {
+ annotationsToRetain.add(annotation);
+ }
+
+ public AnnotationRemover build(AppView<AppInfoWithLiveness> appView) {
+ assert classesToRetainInnerClassAttributeFor != null;
+ return new AnnotationRemover(
+ appView, classesToRetainInnerClassAttributeFor, annotationsToRetain);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java
new file mode 100644
index 0000000..2e5edba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
+
+class ConsequentRootSetBuilder extends RootSetBuilder {
+
+ private final Enqueuer enqueuer;
+
+ ConsequentRootSetBuilder(AppView<? extends AppInfoWithSubtyping> appView, Enqueuer enqueuer) {
+ super(appView, appView.appInfo().app(), null);
+ this.enqueuer = enqueuer;
+ }
+
+ @Override
+ void handleMatchedAnnotation(AnnotationMatchResult annotationMatchResult) {
+ if (enqueuer.getMode().isInitialTreeShaking()
+ && annotationMatchResult.isConcreteAnnotationMatchResult()) {
+ enqueuer.retainAnnotationForFinalTreeShaking(
+ annotationMatchResult.asConcreteAnnotationMatchResult().getMatchedAnnotation());
+ }
+ }
+}
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 9a33da2..1a0bc08 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -144,6 +144,7 @@
private RootSet rootSet;
private ProguardClassFilter dontWarnPatterns;
private final EnqueuerUseRegistryFactory useRegistryFactory;
+ private AnnotationRemover.Builder annotationRemoverBuilder;
private final Map<DexMethod, Set<DexEncodedMethod>> virtualInvokes = new IdentityHashMap<>();
private final Map<DexMethod, Set<DexEncodedMethod>> interfaceInvokes = new IdentityHashMap<>();
@@ -356,6 +357,10 @@
return this;
}
+ public void setAnnotationRemoverBuilder(AnnotationRemover.Builder annotationRemoverBuilder) {
+ this.annotationRemoverBuilder = annotationRemoverBuilder;
+ }
+
private boolean isProgramClass(DexType type) {
return getProgramClassOrNull(type) != null;
}
@@ -1319,7 +1324,7 @@
DexClass clazz = appView.definitionFor(type);
boolean annotationTypeIsLibraryClass = clazz == null || clazz.isNotProgramClass();
boolean isLive = annotationTypeIsLibraryClass || liveTypes.contains(clazz.asProgramClass());
- if (!shouldKeepAnnotation(holder, annotation, isLive, appView)) {
+ if (!shouldKeepAnnotation(appView, holder, annotation, isLive)) {
// Remember this annotation for later.
if (!annotationTypeIsLibraryClass) {
deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
@@ -2395,7 +2400,8 @@
activeIfRules.computeIfAbsent(wrap, ignore -> new LinkedHashSet<>()).add(ifRule);
}
}
- RootSetBuilder consequentSetBuilder = new RootSetBuilder(appView);
+ ConsequentRootSetBuilder consequentSetBuilder =
+ new ConsequentRootSetBuilder(appView, this);
IfRuleEvaluator ifRuleEvaluator =
new IfRuleEvaluator(
appView, this, executorService, activeIfRules, consequentSetBuilder);
@@ -2524,6 +2530,13 @@
lambdaMethodsTargetedByInvokeDynamic.clear();
}
+ void retainAnnotationForFinalTreeShaking(DexAnnotation annotation) {
+ assert mode.isInitialTreeShaking();
+ if (annotationRemoverBuilder != null) {
+ annotationRemoverBuilder.retainAnnotation(annotation);
+ }
+ }
+
// Package protected due to entry point from worklist.
void markMethodAsKept(DexProgramClass holder, DexEncodedMethod target, KeepReason reason) {
DexMethod method = target.method;
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index c1dabc8..fe81bb7 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -14,6 +16,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions.ProguardIfRuleEvaluationData;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
@@ -37,14 +40,14 @@
private final ExecutorService executorService;
private final List<Future<?>> futures = new ArrayList<>();
private final Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRules;
- private final RootSetBuilder rootSetBuilder;
+ private final ConsequentRootSetBuilder rootSetBuilder;
IfRuleEvaluator(
AppView<? extends AppInfoWithSubtyping> appView,
Enqueuer enqueuer,
ExecutorService executorService,
Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRules,
- RootSetBuilder rootSetBuilder) {
+ ConsequentRootSetBuilder rootSetBuilder) {
this.appView = appView;
this.enqueuer = enqueuer;
this.executorService = executorService;
@@ -61,6 +64,8 @@
while (it.hasNext()) {
Map.Entry<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRuleEntry = it.next();
ProguardIfRule ifRule = ifRuleEntry.getKey().get();
+ ProguardIfRuleEvaluationData ifRuleEvaluationData =
+ appView.options().testing.proguardIfRuleEvaluationData;
// Depending on which types that trigger the -if rule, the application of the subsequent
// -keep rule may vary (due to back references). So, we need to try all pairs of -if
@@ -73,12 +78,9 @@
// Check if the class matches the if-rule.
if (appView.options().testing.measureProguardIfRuleEvaluations) {
- appView.options()
- .testing
- .proguardIfRuleEvaluationData
- .numberOfProguardIfRuleClassEvaluations++;
+ ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
}
- if (evaluateClassForIfRule(ifRule, clazz, clazz)) {
+ if (evaluateClassForIfRule(ifRule, clazz)) {
// When matching an if rule against a type, the if-rule are filled with the current
// capture of wildcards. Propagate this down to member rules with same class part
// equivalence.
@@ -88,10 +90,7 @@
memberRule -> {
registerClassCapture(memberRule, clazz, clazz);
if (appView.options().testing.measureProguardIfRuleEvaluations) {
- appView.options()
- .testing
- .proguardIfRuleEvaluationData
- .numberOfProguardIfRuleMemberEvaluations++;
+ ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
}
return evaluateIfRuleMembersAndMaterialize(memberRule, clazz, clazz)
&& canRemoveSubsequentKeepRule(memberRule);
@@ -99,33 +98,30 @@
}
// Check if one of the types that have been merged into `clazz` satisfies the if-rule.
- if (appView.options().enableVerticalClassMerging
- && appView.verticallyMergedClasses() != null) {
+ if (appView.verticallyMergedClasses() != null) {
Iterable<DexType> sources =
appView.verticallyMergedClasses().getSourcesFor(clazz.type);
for (DexType sourceType : sources) {
// Note that, although `sourceType` has been merged into `type`, the dex class for
// `sourceType` is still available until the second round of tree shaking. This
// way we can still retrieve the access flags of `sourceType`.
- DexClass sourceClass = appView.definitionFor(sourceType);
- assert sourceClass != null;
- if (appView.options().testing.measureProguardIfRuleEvaluations) {
- appView.options()
- .testing
- .proguardIfRuleEvaluationData
- .numberOfProguardIfRuleClassEvaluations++;
+ DexProgramClass sourceClass =
+ asProgramClassOrNull(appView.definitionFor(sourceType));
+ if (sourceClass == null) {
+ assert false;
+ continue;
}
- if (evaluateClassForIfRule(ifRule, sourceClass, clazz)) {
+ if (appView.options().testing.measureProguardIfRuleEvaluations) {
+ ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
+ }
+ if (evaluateClassForIfRule(ifRule, sourceClass)) {
ifRuleEntry
.getValue()
.removeIf(
memberRule -> {
registerClassCapture(memberRule, sourceClass, clazz);
if (appView.options().testing.measureProguardIfRuleEvaluations) {
- appView.options()
- .testing
- .proguardIfRuleEvaluationData
- .numberOfProguardIfRuleMemberEvaluations++;
+ ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
}
return evaluateIfRuleMembersAndMaterialize(
memberRule, sourceClass, clazz)
@@ -191,28 +187,25 @@
return false;
}
- /**
- * Determines if `sourceClass` satisfies the given if-rule class specification. If `sourceClass`
- * has not been merged into another class, then `targetClass` is the same as `sourceClass`.
- * Otherwise, `targetClass` denotes the class that `sourceClass` has been merged into.
- */
- private boolean evaluateClassForIfRule(
- ProguardIfRule rule, DexClass sourceClass, DexClass targetClass) {
- if (!RootSetBuilder.satisfyClassType(rule, sourceClass)) {
+ /** Determines if {@param clazz} satisfies the given if-rule class specification. */
+ private boolean evaluateClassForIfRule(ProguardIfRule rule, DexProgramClass clazz) {
+ if (!RootSetBuilder.satisfyClassType(rule, clazz)) {
return false;
}
- if (!RootSetBuilder.satisfyAccessFlag(rule, sourceClass)) {
+ if (!RootSetBuilder.satisfyAccessFlag(rule, clazz)) {
return false;
}
- if (!RootSetBuilder.satisfyAnnotation(rule, sourceClass)) {
+ AnnotationMatchResult annotationMatchResult = RootSetBuilder.satisfyAnnotation(rule, clazz);
+ if (annotationMatchResult == null) {
return false;
}
- if (!rule.getClassNames().matches(sourceClass.type)) {
+ rootSetBuilder.handleMatchedAnnotation(annotationMatchResult);
+ if (!rule.getClassNames().matches(clazz.type)) {
return false;
}
if (rule.hasInheritanceClassName()) {
// Try another live type since the current one doesn't satisfy the inheritance rule.
- return rootSetBuilder.satisfyInheritanceRule(sourceClass, rule);
+ return rootSetBuilder.satisfyInheritanceRule(clazz, rule);
}
return true;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index 93f36a2..a881b83 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -16,6 +16,7 @@
import com.google.common.collect.Iterables;
import java.util.Collections;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -169,38 +170,45 @@
return type;
}
- public boolean matches(DexEncodedField field, AppView<?> appView, DexStringCache stringCache) {
+ public boolean matches(
+ DexEncodedField field,
+ AppView<?> appView,
+ Consumer<AnnotationMatchResult> matchedAnnotationsConsumer,
+ DexStringCache stringCache) {
DexField originalSignature = appView.graphLense().getOriginalFieldSignature(field.field);
switch (getRuleType()) {
case ALL:
case ALL_FIELDS:
- // Access flags check.
- if (!getAccessFlags().containsAll(field.accessFlags)
- || !getNegatedAccessFlags().containsNone(field.accessFlags)) {
- break;
+ {
+ // Access flags check.
+ if (!getAccessFlags().containsAll(field.accessFlags)
+ || !getNegatedAccessFlags().containsNone(field.accessFlags)) {
+ break;
+ }
+ // Annotations check.
+ return RootSetBuilder.containsAnnotation(annotation, field, matchedAnnotationsConsumer);
}
- // Annotations check.
- return RootSetBuilder.containsAnnotation(annotation, field);
+
case FIELD:
- // Name check.
- String name = stringCache.lookupString(originalSignature.name);
- if (!getName().matches(name)) {
- break;
+ {
+ // Name check.
+ String name = stringCache.lookupString(originalSignature.name);
+ if (!getName().matches(name)) {
+ break;
+ }
+ // Access flags check.
+ if (!getAccessFlags().containsAll(field.accessFlags)
+ || !getNegatedAccessFlags().containsNone(field.accessFlags)) {
+ break;
+ }
+ // Type check.
+ if (!getType().matches(originalSignature.type, appView)) {
+ break;
+ }
+ // Annotations check
+ return RootSetBuilder.containsAnnotation(annotation, field, matchedAnnotationsConsumer);
}
- // Access flags check.
- if (!getAccessFlags().containsAll(field.accessFlags)
- || !getNegatedAccessFlags().containsNone(field.accessFlags)) {
- break;
- }
- // Type check.
- if (!getType().matches(originalSignature.type, appView)) {
- break;
- }
- // Annotations check
- if (!RootSetBuilder.containsAnnotation(annotation, field)) {
- break;
- }
- return true;
+
case ALL_METHODS:
case CLINIT:
case INIT:
@@ -211,7 +219,11 @@
return false;
}
- public boolean matches(DexEncodedMethod method, AppView<?> appView, DexStringCache stringCache) {
+ public boolean matches(
+ DexEncodedMethod method,
+ AppView<?> appView,
+ Consumer<AnnotationMatchResult> matchedAnnotationsConsumer,
+ DexStringCache stringCache) {
DexMethod originalSignature = appView.graphLense().getOriginalMethodSignature(method.method);
switch (getRuleType()) {
case ALL_METHODS:
@@ -219,53 +231,61 @@
break;
}
// Fall through for all other methods.
+
case ALL:
- // Access flags check.
- if (!getAccessFlags().containsAll(method.accessFlags)
- || !getNegatedAccessFlags().containsNone(method.accessFlags)) {
- break;
+ {
+ // Access flags check.
+ if (!getAccessFlags().containsAll(method.accessFlags)
+ || !getNegatedAccessFlags().containsNone(method.accessFlags)) {
+ break;
+ }
+ // Annotations check.
+ return RootSetBuilder.containsAnnotation(annotation, method, matchedAnnotationsConsumer);
}
- // Annotations check.
- return RootSetBuilder.containsAnnotation(annotation, method);
+
case METHOD:
// Check return type.
if (!type.matches(originalSignature.proto.returnType, appView)) {
break;
}
// Fall through for access flags, name and arguments.
+
case CONSTRUCTOR:
case INIT:
case CLINIT:
- // Name check.
- String name = stringCache.lookupString(originalSignature.name);
- if (!getName().matches(name)) {
- break;
- }
- // Access flags check.
- if (!getAccessFlags().containsAll(method.accessFlags)
- || !getNegatedAccessFlags().containsNone(method.accessFlags)) {
- break;
- }
- // Annotations check.
- if (!RootSetBuilder.containsAnnotation(annotation, method)) {
- break;
- }
- // Parameter types check.
- List<ProguardTypeMatcher> arguments = getArguments();
- if (arguments.size() == 1 && arguments.get(0).isTripleDotPattern()) {
- return true;
- }
- DexType[] parameters = originalSignature.proto.parameters.values;
- if (parameters.length != arguments.size()) {
- break;
- }
- for (int i = 0; i < parameters.length; i++) {
- if (!arguments.get(i).matches(parameters[i], appView)) {
+ {
+ // Name check.
+ String name = stringCache.lookupString(originalSignature.name);
+ if (!getName().matches(name)) {
+ break;
+ }
+ // Access flags check.
+ if (!getAccessFlags().containsAll(method.accessFlags)
+ || !getNegatedAccessFlags().containsNone(method.accessFlags)) {
+ break;
+ }
+ // Annotations check.
+ if (!RootSetBuilder.containsAnnotation(annotation, method, matchedAnnotationsConsumer)) {
return false;
}
+ // Parameter types check.
+ List<ProguardTypeMatcher> arguments = getArguments();
+ if (arguments.size() == 1 && arguments.get(0).isTripleDotPattern()) {
+ return true;
+ }
+ DexType[] parameters = originalSignature.proto.parameters.values;
+ if (parameters.length != arguments.size()) {
+ break;
+ }
+ for (int i = 0; i < parameters.length; i++) {
+ if (!arguments.get(i).matches(parameters[i], appView)) {
+ return false;
+ }
+ }
+ // All parameters matched.
+ return true;
}
- // All parameters matched.
- return true;
+
case ALL_FIELDS:
case FIELD:
break;
@@ -408,5 +428,4 @@
ruleBuilder.setRuleType(ProguardMemberType.ALL);
return ruleBuilder.build();
}
-
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index f5fc58f..6ae1edb 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -27,6 +27,8 @@
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.AnnotationMatchResult.AnnotationsIgnoredMatchResult;
+import com.android.tools.r8.shaking.AnnotationMatchResult.ConcreteAnnotationMatchResult;
import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.Consumer3;
@@ -62,6 +64,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -120,6 +123,10 @@
this(appView, appView.appInfo().app(), null);
}
+ void handleMatchedAnnotation(AnnotationMatchResult annotation) {
+ // Intentionally empty.
+ }
+
// Process a class with the keep rule.
private void process(
DexClass clazz,
@@ -131,9 +138,11 @@
if (!satisfyAccessFlag(rule, clazz)) {
return;
}
- if (!satisfyAnnotation(rule, clazz)) {
+ AnnotationMatchResult annotationMatchResult = satisfyAnnotation(rule, clazz);
+ if (annotationMatchResult == null) {
return;
}
+ handleMatchedAnnotation(annotationMatchResult);
// In principle it should make a difference whether the user specified in a class
// spec that a class either extends or implements another type. However, proguard
// seems not to care, so users have started to use this inconsistently. We are thus
@@ -143,96 +152,93 @@
return;
}
- if (rule.getClassNames().matches(clazz.type)) {
- Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
- Map<Predicate<DexDefinition>, DexDefinition> preconditionSupplier;
- if (rule instanceof ProguardKeepRule) {
- if (clazz.isNotProgramClass()) {
- return;
- }
- switch (((ProguardKeepRule) rule).getType()) {
- case KEEP_CLASS_MEMBERS:
- // Members mentioned at -keepclassmembers always depend on their holder.
- preconditionSupplier = ImmutableMap.of(definition -> true, clazz);
- markMatchingVisibleMethods(
- clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
- markMatchingVisibleFields(
- clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
- break;
- case KEEP_CLASSES_WITH_MEMBERS:
- if (!allRulesSatisfied(memberKeepRules, clazz)) {
- break;
- }
- // fallthrough;
- case KEEP:
- markClass(clazz, rule, ifRule);
- preconditionSupplier = new HashMap<>();
- if (ifRule != null) {
- // Static members in -keep are pinned no matter what.
- preconditionSupplier.put(DexDefinition::isStaticMember, null);
- // Instance members may need to be kept even though the holder is not instantiated.
- preconditionSupplier.put(definition -> !definition.isStaticMember(), clazz);
- } else {
- // Members mentioned at -keep should always be pinned as long as that -keep rule is
- // not triggered conditionally.
- preconditionSupplier.put((definition -> true), null);
- }
- markMatchingVisibleMethods(
- clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
- markMatchingVisibleFields(
- clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
- break;
- case CONDITIONAL:
- throw new Unreachable("-if rule will be evaluated separately, not here.");
- }
+ if (!rule.getClassNames().matches(clazz.type)) {
+ return;
+ }
+
+ Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
+ Map<Predicate<DexDefinition>, DexDefinition> preconditionSupplier;
+ if (rule instanceof ProguardKeepRule) {
+ if (clazz.isNotProgramClass()) {
return;
}
- // Only the ordinary keep rules are supported in a conditional rule.
- assert ifRule == null;
- if (rule instanceof ProguardIfRule) {
- throw new Unreachable("-if rule will be evaluated separately, not here.");
- } else if (rule instanceof ProguardCheckDiscardRule) {
- if (memberKeepRules.isEmpty()) {
- markClass(clazz, rule, ifRule);
- } else {
- preconditionSupplier = ImmutableMap.of((definition -> true), clazz);
+ switch (((ProguardKeepRule) rule).getType()) {
+ case KEEP_CLASS_MEMBERS:
+ // Members mentioned at -keepclassmembers always depend on their holder.
+ preconditionSupplier = ImmutableMap.of(definition -> true, clazz);
markMatchingVisibleMethods(
- clazz, memberKeepRules, rule, preconditionSupplier, true, ifRule);
+ clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
markMatchingVisibleFields(
- clazz, memberKeepRules, rule, preconditionSupplier, true, ifRule);
- }
- } else if (rule instanceof ProguardWhyAreYouKeepingRule) {
- markClass(clazz, rule, ifRule);
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
- markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
- } else if (rule instanceof ProguardAssumeMayHaveSideEffectsRule
- || rule instanceof ProguardAssumeNoSideEffectRule
- || rule instanceof ProguardAssumeValuesRule) {
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
- markMatchingOverriddenMethods(
- appView.appInfo(), clazz, memberKeepRules, rule, null, true, ifRule);
- markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
- } else if (rule instanceof ClassMergingRule) {
- if (allRulesSatisfied(memberKeepRules, clazz)) {
+ clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
+ break;
+ case KEEP_CLASSES_WITH_MEMBERS:
+ if (!allRulesSatisfied(memberKeepRules, clazz)) {
+ break;
+ }
+ // fallthrough;
+ case KEEP:
markClass(clazz, rule, ifRule);
- }
- } else if (rule instanceof InlineRule
- || rule instanceof ConstantArgumentRule
- || rule instanceof UnusedArgumentRule
- || rule instanceof WhyAreYouNotInliningRule) {
- markMatchingMethods(clazz, memberKeepRules, rule, null, ifRule);
- } else if (rule instanceof ClassInlineRule) {
- if (allRulesSatisfied(memberKeepRules, clazz)) {
- markClass(clazz, rule, ifRule);
- }
- } else if (rule instanceof MemberValuePropagationRule) {
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
- markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
- } else {
- assert rule instanceof ProguardIdentifierNameStringRule;
- markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
- markMatchingMethods(clazz, memberKeepRules, rule, null, ifRule);
+ preconditionSupplier = new HashMap<>();
+ if (ifRule != null) {
+ // Static members in -keep are pinned no matter what.
+ preconditionSupplier.put(DexDefinition::isStaticMember, null);
+ // Instance members may need to be kept even though the holder is not instantiated.
+ preconditionSupplier.put(definition -> !definition.isStaticMember(), clazz);
+ } else {
+ // Members mentioned at -keep should always be pinned as long as that -keep rule is
+ // not triggered conditionally.
+ preconditionSupplier.put((definition -> true), null);
+ }
+ markMatchingVisibleMethods(
+ clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
+ markMatchingVisibleFields(
+ clazz, memberKeepRules, rule, preconditionSupplier, false, ifRule);
+ break;
+ case CONDITIONAL:
+ throw new Unreachable("-if rule will be evaluated separately, not here.");
}
+ return;
+ }
+ // Only the ordinary keep rules are supported in a conditional rule.
+ assert ifRule == null;
+ if (rule instanceof ProguardIfRule) {
+ throw new Unreachable("-if rule will be evaluated separately, not here.");
+ } else if (rule instanceof ProguardCheckDiscardRule) {
+ if (memberKeepRules.isEmpty()) {
+ markClass(clazz, rule, ifRule);
+ } else {
+ preconditionSupplier = ImmutableMap.of((definition -> true), clazz);
+ markMatchingVisibleMethods(
+ clazz, memberKeepRules, rule, preconditionSupplier, true, ifRule);
+ markMatchingVisibleFields(clazz, memberKeepRules, rule, preconditionSupplier, true, ifRule);
+ }
+ } else if (rule instanceof ProguardWhyAreYouKeepingRule) {
+ markClass(clazz, rule, ifRule);
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
+ markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
+ } else if (rule instanceof ProguardAssumeMayHaveSideEffectsRule
+ || rule instanceof ProguardAssumeNoSideEffectRule
+ || rule instanceof ProguardAssumeValuesRule) {
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
+ markMatchingOverriddenMethods(
+ appView.appInfo(), clazz, memberKeepRules, rule, null, true, ifRule);
+ markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
+ } else if (rule instanceof InlineRule
+ || rule instanceof ConstantArgumentRule
+ || rule instanceof UnusedArgumentRule
+ || rule instanceof WhyAreYouNotInliningRule) {
+ markMatchingMethods(clazz, memberKeepRules, rule, null, ifRule);
+ } else if (rule instanceof ClassInlineRule || rule instanceof ClassMergingRule) {
+ if (allRulesSatisfied(memberKeepRules, clazz)) {
+ markClass(clazz, rule, ifRule);
+ }
+ } else if (rule instanceof MemberValuePropagationRule) {
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
+ markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
+ } else {
+ assert rule instanceof ProguardIdentifierNameStringRule;
+ markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
+ markMatchingMethods(clazz, memberKeepRules, rule, null, ifRule);
}
}
@@ -496,6 +502,10 @@
this.ifRule = ifRule;
}
+ void handleMatchedAnnotation(AnnotationMatchResult annotationMatchResult) {
+ // Intentionally empty.
+ }
+
void run() {
visitAllSuperInterfaces(originalClazz.type);
}
@@ -522,7 +532,7 @@
continue;
}
for (ProguardMemberRule rule : memberKeepRules) {
- if (rule.matches(method, appView, dexStringCache)) {
+ if (rule.matches(method, appView, this::handleMatchedAnnotation, dexStringCache)) {
tryAndKeepMethodOnClass(method, rule);
}
}
@@ -723,7 +733,7 @@
&& rule.getNegatedClassAccessFlags().containsNone(clazz.accessFlags);
}
- static boolean satisfyAnnotation(ProguardConfigurationRule rule, DexClass clazz) {
+ static AnnotationMatchResult satisfyAnnotation(ProguardConfigurationRule rule, DexClass clazz) {
return containsAnnotation(rule.getClassAnnotation(), clazz);
}
@@ -754,9 +764,13 @@
// TODO(b/110141157): Should the vertical class merger move annotations from the source to
// the target class? If so, it is sufficient only to apply the annotation-matcher to the
// annotations of `class`.
- if (rule.getInheritanceClassName().matches(clazz.type, appView)
- && containsAnnotation(rule.getInheritanceAnnotation(), clazz)) {
- return true;
+ if (rule.getInheritanceClassName().matches(clazz.type, appView)) {
+ AnnotationMatchResult annotationMatchResult =
+ containsAnnotation(rule.getInheritanceAnnotation(), clazz);
+ if (annotationMatchResult != null) {
+ handleMatchedAnnotation(annotationMatchResult);
+ return true;
+ }
}
type = clazz.superType;
}
@@ -787,9 +801,13 @@
// TODO(b/110141157): Should the vertical class merger move annotations from the source to
// the target class? If so, it is sufficient only to apply the annotation-matcher to the
// annotations of `ifaceClass`.
- if (rule.getInheritanceClassName().matches(iface, appView)
- && containsAnnotation(rule.getInheritanceAnnotation(), ifaceClass)) {
- return true;
+ if (rule.getInheritanceClassName().matches(iface, appView)) {
+ AnnotationMatchResult annotationMatchResult =
+ containsAnnotation(rule.getInheritanceAnnotation(), ifaceClass);
+ if (annotationMatchResult != null) {
+ handleMatchedAnnotation(annotationMatchResult);
+ return true;
+ }
}
if (anyImplementedInterfaceMatchesImplementsRule(ifaceClass, rule)) {
return true;
@@ -842,7 +860,7 @@
boolean ruleSatisfiedByMethods(ProguardMemberRule rule, Iterable<DexEncodedMethod> methods) {
if (rule.getRuleType().includesMethods()) {
for (DexEncodedMethod method : methods) {
- if (rule.matches(method, appView, dexStringCache)) {
+ if (rule.matches(method, appView, this::handleMatchedAnnotation, dexStringCache)) {
return true;
}
}
@@ -853,7 +871,7 @@
boolean ruleSatisfiedByFields(ProguardMemberRule rule, Iterable<DexEncodedField> fields) {
if (rule.getRuleType().includesFields()) {
for (DexEncodedField field : fields) {
- if (rule.matches(field, appView, dexStringCache)) {
+ if (rule.matches(field, appView, this::handleMatchedAnnotation, dexStringCache)) {
return true;
}
}
@@ -861,40 +879,56 @@
return false;
}
- static boolean containsAnnotation(ProguardTypeMatcher classAnnotation, DexClass clazz) {
+ static AnnotationMatchResult containsAnnotation(
+ ProguardTypeMatcher classAnnotation, DexClass clazz) {
return containsAnnotation(classAnnotation, clazz.annotations);
}
- static boolean containsAnnotation(ProguardTypeMatcher classAnnotation, DexEncodedMethod method) {
- if (containsAnnotation(classAnnotation, method.annotations)) {
+ static boolean containsAnnotation(
+ ProguardTypeMatcher classAnnotation,
+ DexEncodedField field,
+ Consumer<AnnotationMatchResult> matchedAnnotationsConsumer) {
+ AnnotationMatchResult annotationMatchResult =
+ containsAnnotation(classAnnotation, field.annotations);
+ if (annotationMatchResult != null) {
+ matchedAnnotationsConsumer.accept(annotationMatchResult);
+ return true;
+ }
+ return false;
+ }
+
+ static boolean containsAnnotation(
+ ProguardTypeMatcher classAnnotation,
+ DexEncodedMethod method,
+ Consumer<AnnotationMatchResult> matchedAnnotationsConsumer) {
+ AnnotationMatchResult annotationMatchResult =
+ containsAnnotation(classAnnotation, method.annotations);
+ if (annotationMatchResult != null) {
+ matchedAnnotationsConsumer.accept(annotationMatchResult);
return true;
}
for (int i = 0; i < method.parameterAnnotationsList.size(); i++) {
- if (containsAnnotation(classAnnotation, method.parameterAnnotationsList.get(i))) {
+ annotationMatchResult =
+ containsAnnotation(classAnnotation, method.parameterAnnotationsList.get(i));
+ if (annotationMatchResult != null) {
+ matchedAnnotationsConsumer.accept(annotationMatchResult);
return true;
}
}
return false;
}
- static boolean containsAnnotation(ProguardTypeMatcher classAnnotation, DexEncodedField field) {
- return containsAnnotation(classAnnotation, field.annotations);
- }
-
- private static boolean containsAnnotation(
+ private static AnnotationMatchResult containsAnnotation(
ProguardTypeMatcher classAnnotation, DexAnnotationSet annotations) {
if (classAnnotation == null) {
- return true;
- }
- if (annotations.isEmpty()) {
- return false;
+ return AnnotationsIgnoredMatchResult.getInstance();
}
for (DexAnnotation annotation : annotations.annotations) {
if (classAnnotation.matches(annotation.annotation.type)) {
- return true;
+ return new ConcreteAnnotationMatchResult(annotation);
}
}
- return false;
+ return null;
}
private void markMethod(
@@ -910,7 +944,7 @@
return;
}
for (ProguardMemberRule rule : rules) {
- if (rule.matches(method, appView, dexStringCache)) {
+ if (rule.matches(method, appView, this::handleMatchedAnnotation, dexStringCache)) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
rule);
@@ -930,7 +964,7 @@
DexDefinition precondition,
ProguardIfRule ifRule) {
for (ProguardMemberRule rule : rules) {
- if (rule.matches(field, appView, dexStringCache)) {
+ if (rule.matches(field, appView, this::handleMatchedAnnotation, dexStringCache)) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Marking field `%s` due to `%s { %s }`.", field, context,
rule);
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 9ed931c..2de4c37 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -435,6 +435,11 @@
return enableMinification;
}
+ public boolean keepInnerClassStructure() {
+ return getProguardConfiguration().getKeepAttributes().signature
+ || getProguardConfiguration().getKeepAttributes().innerClasses;
+ }
+
public boolean printCfg = false;
public String printCfgFile;
public boolean ignoreMissingClasses = false;
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/B149729626.java b/src/test/java/com/android/tools/r8/shaking/annotations/B149729626.java
index e0c497c..5b8f418 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/B149729626.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/B149729626.java
@@ -43,11 +43,7 @@
.addKeepRules(
"-keepclassmembers @" + Marker.class.getTypeName() + " class * {",
" <init>(...);",
- "}",
- // TODO(b/149729626): Should not be required.
- "-keep class " + TestClass.class.getTypeName() + " { void makeMarkerLive(); }")
- // TODO(b/149729626): Should not be required.
- .addKeepRuntimeVisibleAnnotations()
+ "}")
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -64,11 +60,7 @@
"-if @" + Marker.class.getTypeName() + " class *",
"-keep class <1> {",
" <init>(...);",
- "}",
- // TODO(b/149729626): Should not be required.
- "-keep class " + TestClass.class.getTypeName() + " { void makeMarkerLive(); }")
- // TODO(b/149729626): Should not be required.
- .addKeepRuntimeVisibleAnnotations()
+ "}")
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -99,10 +91,6 @@
public static void main(String[] args) {
System.out.println(Marked.class);
}
-
- static void makeMarkerLive() {
- System.out.println(Marker.class);
- }
}
@Target(ElementType.TYPE)