Version 1.3.42
Merge: Keep annotations for targeted methods
CL: https://r8-review.googlesource.com/c/r8/+/31402
Bug: 120168590
Change-Id: Iac98b6a27f92a0895ca5b68cd8b4081ad74806b0
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7bed8ae..e218f3d 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 @@
.prunedCopyFrom(application, pruner.getRemovedClasses()));
new AbstractMethodRemover(appView.getAppInfo()).run();
}
- new AnnotationRemover(appView.getAppInfo().withLiveness(), compatibility, options).run();
+
+ new AnnotationRemover(appView.getAppInfo().withLiveness(), options)
+ .ensureValid(compatibility)
+ .run();
// TODO(69445518): This is still work in progress, and this file writing is currently used
// for testing.
@@ -462,6 +465,8 @@
// Print reasons on the application after pruning, so that we reflect the actual result.
ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(rootSet.reasonAsked);
reasonPrinter.run(application);
+ // Remove annotations that refer to types that no longer exist.
+ new AnnotationRemover(appView.getAppInfo().withLiveness(), options).run();
}
} finally {
timing.end();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 3ea008a..f99ce40 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 = "1.3.41";
+ public static final String LABEL = "1.3.42";
private Version() {
}
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 459fe7a..00e09a4 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -19,13 +19,10 @@
private final AppInfoWithLiveness appInfo;
private final ProguardKeepAttributes keep;
private final InternalOptions options;
- private final ProguardConfiguration.Builder compatibility;
- public AnnotationRemover(AppInfoWithLiveness appInfo,
- ProguardConfiguration.Builder compatibility, InternalOptions options) {
+ public AnnotationRemover(AppInfoWithLiveness appInfo, InternalOptions options) {
this.appInfo = appInfo;
this.keep = options.proguardConfiguration.getKeepAttributes();
- this.compatibility = compatibility;
this.options = options;
}
@@ -118,8 +115,12 @@
return isAnnotationTypeLive(annotation);
}
- public void run() {
+ public AnnotationRemover ensureValid(ProguardConfiguration.Builder compatibility) {
keep.ensureValid(options.forceProguardCompatibility, compatibility);
+ return this;
+ }
+
+ public void run() {
for (DexProgramClass clazz : appInfo.classes()) {
stripAttributes(clazz);
clazz.annotations = clazz.annotations.keepIf(this::filterAnnotations);
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 f43dab5..d32402a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -814,20 +814,24 @@
}
}
- private void markMethodAsTargeted(DexEncodedMethod encodedMethod, KeepReason reason) {
- markTypeAsLive(encodedMethod.method.holder);
- markParameterAndReturnTypesAsLive(encodedMethod);
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Method `%s` is targeted.", encodedMethod.method);
+ private void markMethodAsTargeted(DexEncodedMethod method, KeepReason reason) {
+ if (!targetedMethods.add(method, reason)) {
+ return;
}
- targetedMethods.add(encodedMethod, reason);
+ markTypeAsLive(method.method.holder);
+ markParameterAndReturnTypesAsLive(method);
+ processAnnotations(method.annotations.annotations);
+ method.parameterAnnotationsList.forEachAnnotation(this::processAnnotation);
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Method `%s` is targeted.", method.method);
+ }
if (forceProguardCompatibility) {
// Keep targeted default methods in compatibility mode. The tree pruner will otherwise make
// these methods abstract, whereas Proguard does not (seem to) touch their code.
- DexClass clazz = appInfo.definitionFor(encodedMethod.method.holder);
- if (!encodedMethod.accessFlags.isAbstract()
+ DexClass clazz = appInfo.definitionFor(method.method.holder);
+ if (!method.accessFlags.isAbstract()
&& clazz.isInterface() && !clazz.isLibraryClass()) {
- markMethodAsKeptWithCompatRule(encodedMethod);
+ markMethodAsKeptWithCompatRule(method);
}
}
}
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
new file mode 100644
index 0000000..a7bdf0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.annotations;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.StringUtils;
+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.lang.reflect.Method;
+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 AnnotationsOnTargetedMethodTest extends TestBase {
+
+ private static final String expectedOutput =
+ StringUtils.lines(
+ "In InterfaceImpl.targetedMethod()",
+ "In OtherInterfaceImpl.targetedMethod()",
+ MyAnnotation.class.getName());
+
+ private final Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public AnnotationsOnTargetedMethodTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void jvmTest() throws Exception {
+ assumeTrue(
+ "JVM test independent of Art version - only run when testing on latest",
+ ToolHelper.getDexVm().getVersion().isLatest());
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+ }
+
+ @Test
+ public void r8Test() throws Exception {
+ testForR8(backend)
+ .addInnerClasses(AnnotationsOnTargetedMethodTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-keepattributes *Annotation*", "-dontobfuscate")
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ test(new InterfaceImpl());
+ test(new OtherInterfaceImpl());
+
+ Method method = Interface.class.getDeclaredMethods()[0];
+ for (Annotation annotation : method.getAnnotations()) {
+ visitAnnotation((MyAnnotation) annotation);
+ }
+ }
+
+ @NeverInline
+ private static void test(Interface obj) {
+ obj.targetedMethod();
+ }
+
+ @NeverInline
+ private static void visitAnnotation(MyAnnotation annotation) {
+ System.out.println(annotation.annotationType().getName());
+ }
+ }
+
+ @NeverMerge
+ interface Interface {
+
+ @NeverInline
+ @MyAnnotation
+ void targetedMethod();
+ }
+
+ static class InterfaceImpl implements Interface {
+
+ @NeverInline
+ @Override
+ public void targetedMethod() {
+ System.out.println("In InterfaceImpl.targetedMethod()");
+ }
+ }
+
+ static class OtherInterfaceImpl implements Interface {
+
+ @NeverInline
+ @Override
+ public void targetedMethod() {
+ System.out.println("In OtherInterfaceImpl.targetedMethod()");
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD})
+ @interface MyAnnotation {}
+}