Reproduce unused argument removal bug in presence of annotations
Bug: 131663970
Change-Id: Ia6572d253920766bbae2a755237052dcdf559e55
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 1fb8f0e..8757da5 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import static com.android.tools.r8.D8Command.USAGE_MESSAGE;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
@@ -206,7 +207,7 @@
.write(executor);
options.printWarnings();
} catch (ExecutionException e) {
- throw R8.unwrapExecutionException(e);
+ throw unwrapExecutionException(e);
} finally {
options.signalFinishedToConsumers();
// Dump timings.
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index f6abea5..404aa4b 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -4,6 +4,8 @@
package com.android.tools.r8;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
@@ -106,7 +108,7 @@
writer.write(executor);
options.printWarnings();
} catch (ExecutionException e) {
- throw R8.unwrapExecutionException(e);
+ throw unwrapExecutionException(e);
} finally {
options.signalFinishedToConsumers();
}
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 19e84d8..34e67e6 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -4,6 +4,8 @@
package com.android.tools.r8;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
@@ -112,7 +114,7 @@
}
}
} catch (ExecutionException e) {
- throw R8.unwrapExecutionException(e);
+ throw unwrapExecutionException(e);
} catch (FeatureMappingException e) {
options.reporter.error(e.getMessage());
} finally {
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index e5842df..6c5ed9a 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -99,7 +101,7 @@
return result;
} catch (ExecutionException e) {
- throw R8.unwrapExecutionException(e);
+ throw unwrapExecutionException(e);
}
}
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 050e69f..d151496 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppServices;
@@ -96,7 +98,7 @@
RootSetBuilder.writeSeeds(
appInfo, System.out, type -> descriptors.contains(type.toDescriptorString()));
} catch (ExecutionException e) {
- throw R8.unwrapExecutionException(e);
+ throw unwrapExecutionException(e);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 458972d..16b75f5 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import static com.android.tools.r8.R8Command.USAGE_MESSAGE;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
@@ -821,20 +822,6 @@
}
}
- static RuntimeException unwrapExecutionException(ExecutionException executionException) {
- Throwable cause = executionException.getCause();
- if (cause instanceof Error) {
- // add original exception as suppressed exception to provide the original stack trace
- cause.addSuppressed(executionException);
- throw (Error) cause;
- } else if (cause instanceof RuntimeException) {
- cause.addSuppressed(executionException);
- throw (RuntimeException) cause;
- } else {
- throw new RuntimeException(executionException.getMessage(), cause);
- }
- }
-
private static void run(String[] args) throws CompilationFailedException {
R8Command command = R8Command.parse(args, CommandLineOrigin.INSTANCE).build();
if (command.isPrintHelp()) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 20eafd2..3c239c8 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.ClassKind.CLASSPATH;
import static com.android.tools.r8.graph.ClassKind.LIBRARY;
import static com.android.tools.r8.graph.ClassKind.PROGRAM;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.DataResourceProvider;
@@ -123,6 +124,8 @@
builder.addDataResourceProvider(dataResourceProvider);
}
}
+ } catch (ExecutionException e) {
+ throw unwrapExecutionException(e);
} catch (ResourceException e) {
throw options.reporter.fatalError(new StringDiagnostic(e.getMessage(), e.getOrigin()));
} finally {
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index d77adfd..c56ecf4 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -13,6 +13,7 @@
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
public abstract class ExceptionUtils {
@@ -105,4 +106,17 @@
return Origin.unknown();
}
+ public static RuntimeException unwrapExecutionException(ExecutionException executionException) {
+ Throwable cause = executionException.getCause();
+ if (cause instanceof Error) {
+ // add original exception as suppressed exception to provide the original stack trace
+ cause.addSuppressed(executionException);
+ throw (Error) cause;
+ } else if (cause instanceof RuntimeException) {
+ cause.addSuppressed(executionException);
+ throw (RuntimeException) cause;
+ } else {
+ throw new RuntimeException(executionException.getMessage(), cause);
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 0c441a5..14d6d72 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -232,10 +232,18 @@
}
public T enableUnusedArgumentAnnotations() {
- if (!enableUnusedArgumentAnnotations) {
- enableUnusedArgumentAnnotations = true;
- addInternalKeepRules(
- "-keepunusedarguments class * { @com.android.tools.r8.KeepUnusedArguments *; }");
+ return enableUnusedArgumentAnnotations(true);
+ }
+
+ public T enableUnusedArgumentAnnotations(boolean value) {
+ if (value) {
+ if (!enableUnusedArgumentAnnotations) {
+ enableUnusedArgumentAnnotations = true;
+ addInternalKeepRules(
+ "-keepunusedarguments class * { @com.android.tools.r8.KeepUnusedArguments *; }");
+ }
+ } else {
+ assert !enableUnusedArgumentAnnotations;
}
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 8b7dbbb..cfb3c11 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -132,8 +132,12 @@
return self();
}
+ public T addKeepAttributes(String... attributes) {
+ return addKeepRules("-keepattributes " + String.join(",", attributes));
+ }
+
public T addKeepAllAttributes() {
- return addKeepRules("-keepattributes *");
+ return addKeepAttributes("*");
}
public abstract T addApplyMapping(String proguardMap);
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
new file mode 100644
index 0000000..c0eaca3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2019, 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.ir.optimize.unusedarguments;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.KeepUnusedArguments;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+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;
+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 UnusedAnnotatedArgumentsTest extends TestBase {
+
+ private final boolean keepUnusedArguments;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{1}, keep unused arguments: {0}")
+ public static List<Object[]> params() {
+ return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ }
+
+ public UnusedAnnotatedArgumentsTest(boolean keepUnusedArguments, TestParameters parameters) {
+ this.keepUnusedArguments = keepUnusedArguments;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ try {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(UnusedAnnotatedArgumentsTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(Unused.class)
+ .addKeepAttributes("RuntimeVisibleParameterAnnotations")
+ .enableInliningAnnotations()
+ .enableUnusedArgumentAnnotations(keepUnusedArguments)
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .inspect(this::verifyOutput)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ assertTrue(keepUnusedArguments);
+ } catch (ArrayIndexOutOfBoundsException | AssertionError e) {
+ // TODO(b/131663970): Fix unused argument removal.
+ assertFalse(keepUnusedArguments);
+ }
+ }
+
+ private void verifyOutput(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject methodSubject =
+ // TODO(b/123060011): Mapping not working in presence of unused argument removal.
+ classSubject.uniqueMethodWithName(keepUnusedArguments ? "test" : "a");
+ assertThat(methodSubject, isPresent());
+
+ if (keepUnusedArguments) {
+ assertEquals(1, methodSubject.getMethod().method.proto.parameters.size());
+ assertEquals(1, methodSubject.getMethod().parameterAnnotationsList.size());
+ } else {
+ assertEquals(0, methodSubject.getMethod().method.proto.parameters.size());
+ assertEquals(0, methodSubject.getMethod().parameterAnnotationsList.size());
+ }
+
+ System.out.println();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ test(null);
+ }
+
+ @KeepUnusedArguments
+ @NeverInline
+ static void test(@Unused Object unused) {
+ System.out.println("Hello world!");
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.PARAMETER)
+ @interface Unused {}
+}