Merge "Add tests for Proguard's behavior regarding -allowaccessmodification."
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 000076e..c098fb8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1413,6 +1413,11 @@
return result.stdout;
}
+ public static ProcessResult runProguardRaw(
+ Path inJar, Path outJar, Path lib, Path config, Path map) throws IOException {
+ return runProguardRaw(getProguardScript(), inJar, outJar, lib, ImmutableList.of(config), map);
+ }
+
public static ProcessResult runProguardRaw(Path inJar, Path outJar, List<Path> config, Path map)
throws IOException {
return runProguardRaw(getProguardScript(), inJar, outJar, config, map);
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
index c82bbc9..9a9ca34 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
@@ -4,52 +4,303 @@
package com.android.tools.r8.naming.b72391662;
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderThan;
import com.android.tools.r8.naming.b72391662.subpackage.OtherPackageSuper;
import com.android.tools.r8.naming.b72391662.subpackage.OtherPackageTestClass;
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(VmTestRunner.class)
-public class B72391662 extends TestBase {
+public class B72391662 extends ProguardCompatabilityTestBase {
- private void doTest(boolean allowAccessModification, boolean minify) throws Exception {
+ private static final List<Class> CLASSES = ImmutableList.of(
+ TestMain.class, Interface.class, Super.class, TestClass.class,
+ OtherPackageSuper.class, OtherPackageTestClass.class);
+
+ private void doTest_keepAll(
+ Shrinker shrinker,
+ String repackagePrefix,
+ boolean allowAccessModification,
+ boolean minify) throws Exception {
Class mainClass = TestMain.class;
+ String keep = !minify ? "-keep" : "-keep,allowobfuscation";
List<String> config = ImmutableList.of(
- allowAccessModification ?"-allowaccessmodification" : "",
+ "-printmapping",
+ repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
+ allowAccessModification ? "-allowaccessmodification" : "",
!minify ? "-dontobfuscate" : "",
"-keep class " + mainClass.getCanonicalName() + " {",
" public void main(java.lang.String[]);",
"}",
- "-keep class " + TestClass.class.getCanonicalName() + " {",
+ keep + " class " + TestClass.class.getCanonicalName() + " {",
" *;",
"}",
- "-keep class " + OtherPackageTestClass.class.getCanonicalName() + " {",
+ keep + " class " + OtherPackageTestClass.class.getCanonicalName() + " {",
" *;",
- "}"
+ "}",
+ "-dontwarn java.lang.invoke.*"
);
- AndroidApp app = readClassesAndAndriodJar(ImmutableList.of(
- mainClass, Interface.class, Super.class, TestClass.class,
- OtherPackageSuper.class, OtherPackageTestClass.class));
- app = compileWithR8(app, String.join(System.lineSeparator(), config));
+ AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
+
+ DexInspector dexInspector =
+ isR8(shrinker) ? new DexInspector(app) : new DexInspector(app, proguardMap);
+ ClassSubject testClass = dexInspector.clazz(TestClass.class);
+ assertThat(testClass, isPresent());
+
+ // Test the totally unused method.
+ MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+ assertThat(staticMethod, isPresent());
+ assertEquals(minify, staticMethod.isRenamed());
+ if (isR8(shrinker)) {
+ assertEquals(allowAccessModification, staticMethod.getMethod().accessFlags.isPublic());
+ } else {
+ assertFalse(staticMethod.getMethod().accessFlags.isPublic());
+ }
+
+ // Test an indirectly referred method.
+ staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+ assertThat(staticMethod, isPresent());
+ assertEquals(minify, staticMethod.isRenamed());
+ boolean publicizeCondition = isR8(shrinker) ? allowAccessModification
+ : minify && repackagePrefix != null && allowAccessModification;
+ assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
}
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
- public void test() throws Exception {
- doTest(true, true);
- doTest(true, false);
- doTest(false, true);
- doTest(false, false);
+ public void test_keepAll_R8() throws Exception {
+ doTest_keepAll(Shrinker.R8, "r8", true, true);
+ doTest_keepAll(Shrinker.R8, "r8", true, false);
+ doTest_keepAll(Shrinker.R8, "r8", false, true);
+ doTest_keepAll(Shrinker.R8, "r8", false, false);
+ doTest_keepAll(Shrinker.R8, null, true, true);
+ doTest_keepAll(Shrinker.R8, null, true, false);
+ doTest_keepAll(Shrinker.R8, null, false, true);
+ doTest_keepAll(Shrinker.R8, null, false, false);
+ }
+
+ @Ignore("b/92236970")
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepAll_R8Compat() throws Exception {
+ doTest_keepAll(Shrinker.R8_COMPAT, "rc", true, true);
+ doTest_keepAll(Shrinker.R8_COMPAT, "rc", true, false);
+ doTest_keepAll(Shrinker.R8_COMPAT, "rc", false, true);
+ doTest_keepAll(Shrinker.R8_COMPAT, "rc", false, false);
+ doTest_keepAll(Shrinker.R8_COMPAT, null, true, true);
+ doTest_keepAll(Shrinker.R8_COMPAT, null, true, false);
+ doTest_keepAll(Shrinker.R8_COMPAT, null, false, true);
+ doTest_keepAll(Shrinker.R8_COMPAT, null, false, false);
+ }
+
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepAll_Proguard6() throws Exception {
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", true, true);
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", true, false);
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", false, true);
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", false, false);
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, true, true);
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, true, false);
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, false, true);
+ doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, false, false);
+ }
+
+ private void doTest_keepNonPublic(
+ Shrinker shrinker,
+ String repackagePrefix,
+ boolean allowAccessModification,
+ boolean minify) throws Exception {
+ Class mainClass = TestMain.class;
+ String keep = !minify ? "-keep" : "-keep,allowobfuscation";
+ List<String> config = ImmutableList.of(
+ "-printmapping",
+ repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
+ allowAccessModification ? "-allowaccessmodification" : "",
+ !minify ? "-dontobfuscate" : "",
+ "-keep class " + mainClass.getCanonicalName() + " {",
+ " public void main(java.lang.String[]);",
+ "}",
+ keep + " class " + TestClass.class.getCanonicalName() + " {",
+ " !public <methods>;",
+ "}",
+ keep + " class " + OtherPackageTestClass.class.getCanonicalName() + " {",
+ " !public <methods>;",
+ "}",
+ "-dontwarn java.lang.invoke.*"
+ );
+
+ AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
+ assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
+
+ DexInspector dexInspector =
+ isR8(shrinker) ? new DexInspector(app) : new DexInspector(app, proguardMap);
+ ClassSubject testClass = dexInspector.clazz(TestClass.class);
+ assertThat(testClass, isPresent());
+
+ // Test the totally unused method.
+ MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+ assertThat(staticMethod, isPresent());
+ assertEquals(minify, staticMethod.isRenamed());
+ if (isR8(shrinker)) {
+ assertEquals(allowAccessModification, staticMethod.getMethod().accessFlags.isPublic());
+ } else {
+ assertFalse(staticMethod.getMethod().accessFlags.isPublic());
+ }
+
+ // Test an indirectly referred method.
+ staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+ assertThat(staticMethod, isPresent());
+ assertEquals(minify, staticMethod.isRenamed());
+ boolean publicizeCondition = isR8(shrinker) ? allowAccessModification
+ : minify && repackagePrefix != null && allowAccessModification;
+ assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
+ }
+
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepNonPublic_R8() throws Exception {
+ doTest_keepNonPublic(Shrinker.R8, "r8", true, true);
+ doTest_keepNonPublic(Shrinker.R8, "r8", true, false);
+ doTest_keepNonPublic(Shrinker.R8, "r8", false, true);
+ doTest_keepNonPublic(Shrinker.R8, "r8", false, false);
+ doTest_keepNonPublic(Shrinker.R8, null, true, true);
+ doTest_keepNonPublic(Shrinker.R8, null, true, false);
+ doTest_keepNonPublic(Shrinker.R8, null, false, true);
+ doTest_keepNonPublic(Shrinker.R8, null, false, false);
+ }
+
+ @Ignore("b/92236970")
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepNonPublic_R8Compat() throws Exception {
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", true, true);
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", true, false);
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", false, true);
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", false, false);
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, null, true, true);
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, null, true, false);
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, null, false, true);
+ doTest_keepNonPublic(Shrinker.R8_COMPAT, null, false, false);
+ }
+
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepNonPublic_Proguard6() throws Exception {
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, true);
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, false);
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, true);
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, false);
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, true, true);
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, true, false);
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, false, true);
+ doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, false, false);
+ }
+
+ private void doTest_keepPublic(
+ Shrinker shrinker,
+ String repackagePrefix,
+ boolean allowAccessModification,
+ boolean minify) throws Exception {
+ Class mainClass = TestMain.class;
+ String keep = !minify ? "-keep" : "-keep,allowobfuscation";
+ List<String> config = ImmutableList.of(
+ "-printmapping",
+ repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
+ allowAccessModification ? "-allowaccessmodification" : "",
+ !minify ? "-dontobfuscate" : "",
+ "-keep class " + mainClass.getCanonicalName() + " {",
+ " public void main(java.lang.String[]);",
+ "}",
+ keep + " class " + TestClass.class.getCanonicalName() + " {",
+ " public <methods>;",
+ "}",
+ keep + " class " + OtherPackageTestClass.class.getCanonicalName() + " {",
+ " public <methods>;",
+ "}",
+ "-dontwarn java.lang.invoke.*"
+ );
+
+ AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
+ assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
+
+ DexInspector dexInspector =
+ isR8(shrinker) ? new DexInspector(app) : new DexInspector(app, proguardMap);
+ ClassSubject testClass = dexInspector.clazz(TestClass.class);
+ assertThat(testClass, isPresent());
+
+ // Test the totally unused method.
+ MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+ assertThat(staticMethod, not(isPresent()));
+
+ // Test an indirectly referred method.
+ staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+ if (isR8(shrinker)) {
+ // Inlined.
+ assertThat(staticMethod, not(isPresent()));
+ } else {
+ assertThat(staticMethod, isPresent());
+ assertEquals(minify, staticMethod.isRenamed());
+ boolean publicizeCondition = minify && repackagePrefix != null && allowAccessModification;
+ assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
+ }
+ }
+
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepPublic_R8() throws Exception {
+ doTest_keepPublic(Shrinker.R8, "r8", true, true);
+ doTest_keepPublic(Shrinker.R8, "r8", true, false);
+ doTest_keepPublic(Shrinker.R8, "r8", false, true);
+ doTest_keepPublic(Shrinker.R8, "r8", false, false);
+ doTest_keepPublic(Shrinker.R8, null, true, true);
+ doTest_keepPublic(Shrinker.R8, null, true, false);
+ doTest_keepPublic(Shrinker.R8, null, false, true);
+ doTest_keepPublic(Shrinker.R8, null, false, false);
+ }
+
+ @Ignore("b/92236970")
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepPublic_R8Compat() throws Exception {
+ doTest_keepPublic(Shrinker.R8_COMPAT, "rc", true, true);
+ doTest_keepPublic(Shrinker.R8_COMPAT, "rc", true, false);
+ doTest_keepPublic(Shrinker.R8_COMPAT, "rc", false, true);
+ doTest_keepPublic(Shrinker.R8_COMPAT, "rc", false, false);
+ doTest_keepPublic(Shrinker.R8_COMPAT, null, true, true);
+ doTest_keepPublic(Shrinker.R8_COMPAT, null, true, false);
+ doTest_keepPublic(Shrinker.R8_COMPAT, null, false, true);
+ doTest_keepPublic(Shrinker.R8_COMPAT, null, false, false);
+ }
+
+ @Test
+ @IgnoreIfVmOlderThan(Version.V7_0_0)
+ public void test_keepPublic_Proguard6() throws Exception {
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, true);
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, false);
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, true);
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, false);
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, true, true);
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, true, false);
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, false, true);
+ doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, false, false);
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java b/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java
index 88efc09..f13700c 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java
@@ -18,6 +18,10 @@
return value;
}
+ static void unused() {
+ System.out.println("This should be discarded unless there is a keep rule.");
+ }
+
static String staticMethod() {
return "1";
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index 3976f89..e29ceae 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -6,13 +6,16 @@
import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
import com.android.tools.r8.CompatProguardCommandBuilder;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
@@ -24,6 +27,8 @@
public class ProguardCompatabilityTestBase extends TestBase {
+ protected Path proguardMap;
+
public enum Shrinker {
PROGUARD5,
PROGUARD6,
@@ -32,6 +37,34 @@
R8
}
+ protected static boolean isR8(Shrinker shrinker) {
+ return shrinker == Shrinker.R8_COMPAT || shrinker == Shrinker.R8;
+ }
+
+ protected AndroidApp runShrinkerRaw(
+ Shrinker mode, List<Class> programClasses, List<String> proguadConfigs) throws Exception {
+ return runShrinkerRaw(
+ mode, programClasses, String.join(System.lineSeparator(), proguadConfigs));
+ }
+
+ protected AndroidApp runShrinkerRaw(
+ Shrinker mode, List<Class> programClasses, String proguardConfig) throws Exception {
+ proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+ switch (mode) {
+ case PROGUARD5:
+ return runProguard5Raw(programClasses, proguardConfig, proguardMap);
+ case PROGUARD6:
+ return runProguard6Raw(programClasses, proguardConfig, proguardMap);
+ case PROGUARD6_THEN_D8:
+ return runProguard6AndD8Raw(programClasses, proguardConfig, proguardMap);
+ case R8_COMPAT:
+ return runR8CompatRaw(programClasses, proguardConfig);
+ case R8:
+ return runR8Raw(programClasses, proguardConfig);
+ }
+ throw new IllegalArgumentException("Unknown shrinker: " + mode);
+ }
+
protected DexInspector runShrinker(
Shrinker mode, List<Class> programClasses, List<String> proguadConfigs) throws Exception {
return runShrinker(mode, programClasses, String.join(System.lineSeparator(), proguadConfigs));
@@ -54,21 +87,31 @@
throw new IllegalArgumentException("Unknown shrinker: " + mode);
}
- protected DexInspector runR8(List<Class> programClasses, String proguardConfig) throws Exception {
- AndroidApp app = readClasses(programClasses);
+ protected AndroidApp runR8Raw(List<Class> programClasses, String proguardConfig) throws Exception {
+ AndroidApp app = readClassesAndAndriodJar(programClasses);
R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app);
builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
- return new DexInspector(ToolHelper.runR8(builder.build()));
+ return ToolHelper.runR8(builder.build());
}
- protected DexInspector runR8Compat(
+ protected DexInspector runR8(List<Class> programClasses, String proguardConfig) throws Exception {
+ return new DexInspector(runR8Raw(programClasses, proguardConfig));
+ }
+
+ protected AndroidApp runR8CompatRaw(
List<Class> programClasses, String proguardConfig) throws Exception {
CompatProguardCommandBuilder builder = new CompatProguardCommandBuilder(true);
builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
programClasses.forEach(
clazz -> builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz)));
+ builder.addLibraryFiles(ToolHelper.getDefaultAndroidJar());
builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
- return new DexInspector(ToolHelper.runR8(builder.build()));
+ return ToolHelper.runR8(builder.build());
+ }
+
+ protected DexInspector runR8Compat(
+ List<Class> programClasses, String proguardConfig) throws Exception {
+ return new DexInspector(runR8CompatRaw(programClasses, proguardConfig));
}
protected DexInspector runR8CompatKeepingMain(Class mainClass, List<Class> programClasses)
@@ -76,41 +119,79 @@
return runR8Compat(programClasses, keepMainProguardConfiguration(mainClass));
}
- protected DexInspector runProguard5(
- List<Class> programClasses, String proguardConfig) throws Exception {
+ protected AndroidApp runProguard5Raw(
+ List<Class> programClasses, String proguardConfig, Path proguardMap) throws Exception {
Path proguardedJar =
File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
- Path proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
- ToolHelper.runProguard(
- jarTestClasses(programClasses), proguardedJar, proguardConfigFile, proguardMap);
- return new DexInspector(readJar(proguardedJar), proguardMap);
+ ProcessResult result = ToolHelper.runProguardRaw(
+ jarTestClasses(programClasses),
+ proguardedJar,
+ ToolHelper.getAndroidJar(AndroidApiLevel.N),
+ proguardConfigFile,
+ proguardMap);
+ if (result.exitCode != 0) {
+ fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+ }
+ return readJar(proguardedJar);
+ }
+
+ protected DexInspector runProguard5(
+ List<Class> programClasses, String proguardConfig) throws Exception {
+ proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+ return new DexInspector(
+ runProguard5Raw(programClasses, proguardConfig, proguardMap), proguardMap);
+ }
+
+ protected AndroidApp runProguard6Raw(
+ List<Class> programClasses, String proguardConfig, Path proguardMap) throws Exception {
+ Path proguardedJar =
+ File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
+ Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
+ FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
+ ProcessResult result = ToolHelper.runProguard6Raw(
+ jarTestClasses(programClasses),
+ proguardedJar,
+ ToolHelper.getAndroidJar(AndroidApiLevel.N),
+ proguardConfigFile,
+ proguardMap);
+ if (result.exitCode != 0) {
+ fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+ }
+ return readJar(proguardedJar);
}
protected DexInspector runProguard6(
List<Class> programClasses, String proguardConfig) throws Exception {
+ proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+ return new DexInspector(
+ runProguard6Raw(programClasses, proguardConfig, proguardMap), proguardMap);
+ }
+
+ protected AndroidApp runProguard6AndD8Raw(
+ List<Class> programClasses, String proguardConfig, Path proguardMap) throws Exception {
Path proguardedJar =
File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
- Path proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
- ToolHelper.runProguard6(
- jarTestClasses(programClasses), proguardedJar, proguardConfigFile, proguardMap);
- return new DexInspector(readJar(proguardedJar), proguardMap);
+ ProcessResult result = ToolHelper.runProguard6Raw(
+ jarTestClasses(programClasses),
+ proguardedJar,
+ ToolHelper.getAndroidJar(AndroidApiLevel.N),
+ proguardConfigFile,
+ proguardMap);
+ if (result.exitCode != 0) {
+ fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+ }
+ return ToolHelper.runD8(readJar(proguardedJar));
}
protected DexInspector runProguard6AndD8(
List<Class> programClasses, String proguardConfig) throws Exception {
- Path proguardedJar =
- File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
- Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
- Path proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
- FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
- ToolHelper.runProguard6(
- jarTestClasses(programClasses), proguardedJar, proguardConfigFile, proguardMap);
- AndroidApp app = ToolHelper.runD8(readJar(proguardedJar));
- return new DexInspector(app, proguardMap);
+ proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+ return new DexInspector(
+ runProguard6AndD8Raw(programClasses, proguardConfig, proguardMap), proguardMap);
}
protected DexInspector runProguardKeepingMain(Class mainClass, List<Class> programClasses)
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
new file mode 100644
index 0000000..65d1763
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -0,0 +1,195 @@
+// 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.ifrule;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+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 IfOnAccessModifierTest extends ProguardCompatabilityTestBase {
+ private final static List<Class> CLASSES = ImmutableList.of(
+ ClassForIf.class, ClassForSubsequent.class,
+ MainForAccessModifierTest.class);
+
+ private final Shrinker shrinker;
+ private final MethodSignature nonPublicMethod;
+ private final MethodSignature publicMethod;
+
+ public IfOnAccessModifierTest(Shrinker shrinker) {
+ this.shrinker = shrinker;
+ nonPublicMethod = new MethodSignature("nonPublicMethod", "void", ImmutableList.of());
+ publicMethod = new MethodSignature("publicMethod", "void", ImmutableList.of());
+ }
+
+ @Parameters(name = "shrinker: {0}")
+ public static Collection<Object> data() {
+ return ImmutableList.of(Shrinker.PROGUARD6, Shrinker.R8);
+ }
+
+ @Test
+ public void ifOnNonPublic_keepOnPublic() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-printmapping",
+ "-repackageclasses 'top'",
+ "-allowaccessmodification",
+ "-keep class **.Main* {",
+ " public static void callIfNonPublic();",
+ "}",
+ "-if class **.ClassForIf {",
+ " !public <methods>;",
+ "}",
+ "-keep,allowobfuscation class **.ClassForSubsequent {",
+ " public <methods>;",
+ "}"
+ );
+ DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+ assertThat(classSubject, isPresent());
+ MethodSubject methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, not(isPresent()));
+ methodSubject = classSubject.method(nonPublicMethod);
+ assertThat(methodSubject, isPresent());
+ assertTrue(methodSubject.getMethod().accessFlags.isPublic());
+
+ classSubject = dexInspector.clazz(ClassForSubsequent.class);
+ if (isR8(shrinker)) {
+ // TODO(b/72109068): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
+ // at the 2nd tree shaking.
+ assertThat(classSubject, not(isPresent()));
+ return;
+ }
+ assertThat(classSubject, isPresent());
+ methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, isPresent());
+ methodSubject = classSubject.method(nonPublicMethod);
+ assertThat(methodSubject, not(isPresent()));
+ }
+
+ @Test
+ public void ifOnNonPublic_keepOnNonPublic() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-printmapping",
+ "-repackageclasses 'top'",
+ "-allowaccessmodification",
+ "-keep class **.Main* {",
+ " public static void callIfNonPublic();",
+ "}",
+ "-if class **.ClassForIf {",
+ " !public <methods>;",
+ "}",
+ "-keep,allowobfuscation class **.ClassForSubsequent {",
+ " !public <methods>;",
+ "}"
+ );
+ DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+ assertThat(classSubject, isPresent());
+ MethodSubject methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, not(isPresent()));
+ methodSubject = classSubject.method(nonPublicMethod);
+ assertThat(methodSubject, isPresent());
+ assertTrue(methodSubject.getMethod().accessFlags.isPublic());
+
+ classSubject = dexInspector.clazz(ClassForSubsequent.class);
+ if (isR8(shrinker)) {
+ // TODO(b/72109068): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
+ // at the 2nd tree shaking.
+ assertThat(classSubject, not(isPresent()));
+ return;
+ }
+ assertThat(classSubject, isPresent());
+ methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, not(isPresent()));
+ methodSubject = classSubject.method(nonPublicMethod);
+ assertThat(methodSubject, isPresent());
+ assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+ }
+
+ @Test
+ public void ifOnPublic_keepOnPublic() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-printmapping",
+ "-repackageclasses 'top'",
+ "-allowaccessmodification",
+ "-keep class **.Main* {",
+ " public static void callIfPublic();",
+ "}",
+ "-if class **.ClassForIf {",
+ " public <methods>;",
+ "}",
+ "-keep,allowobfuscation class **.ClassForSubsequent {",
+ " public <methods>;",
+ "}"
+ );
+ DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+ assertThat(classSubject, isPresent());
+ MethodSubject methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, isPresent());
+ methodSubject = classSubject.method(nonPublicMethod);
+ assertThat(methodSubject, not(isPresent()));
+
+ classSubject = dexInspector.clazz(ClassForSubsequent.class);
+ assertThat(classSubject, isPresent());
+ methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, isPresent());
+ methodSubject = classSubject.method(nonPublicMethod);
+ assertThat(methodSubject, not(isPresent()));
+ }
+
+ @Test
+ public void ifOnPublic_keepOnNonPublic() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-printmapping",
+ "-repackageclasses 'top'",
+ "-allowaccessmodification",
+ "-keep class **.Main* {",
+ " public static void callIfPublic();",
+ "}",
+ "-if class **.ClassForIf {",
+ " public <methods>;",
+ "}",
+ "-keep,allowobfuscation class **.ClassForSubsequent {",
+ " !public <methods>;",
+ "}"
+ );
+ DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+ assertThat(classSubject, isPresent());
+ MethodSubject methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, isPresent());
+ methodSubject = classSubject.method(nonPublicMethod);
+ assertThat(methodSubject, not(isPresent()));
+
+ classSubject = dexInspector.clazz(ClassForSubsequent.class);
+ assertThat(classSubject, isPresent());
+ methodSubject = classSubject.method(publicMethod);
+ assertThat(methodSubject, not(isPresent()));
+ methodSubject = classSubject.method(nonPublicMethod);
+ if (isR8(shrinker)) {
+ // TODO(b/72109068): if kept in the 1st tree shaking, should be kept after publicizing.
+ assertThat(methodSubject, not(isPresent()));
+ return;
+ }
+ assertThat(methodSubject, isPresent());
+ assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTestClasses.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTestClasses.java
new file mode 100644
index 0000000..1b5bd75
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTestClasses.java
@@ -0,0 +1,40 @@
+// 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.ifrule;
+
+class ClassForIf {
+ ClassForIf() {
+ }
+
+ synchronized void nonPublicMethod() {
+ System.out.println("ClassForIf::nonPublicMethod");
+ }
+
+ synchronized public void publicMethod() {
+ System.out.println("ClassForIf::publicMethod");
+ }
+}
+
+class ClassForSubsequent {
+ ClassForSubsequent() {
+ }
+
+ synchronized void nonPublicMethod() {
+ System.out.println("ClassForSubsequent::nonPublicMethod");
+ }
+
+ synchronized public void publicMethod() {
+ System.out.println("ClassForSubsequent::publicMethod");
+ }
+}
+
+class MainForAccessModifierTest {
+ public static void callIfNonPublic() {
+ new ClassForIf().nonPublicMethod();
+ }
+
+ public static void callIfPublic() {
+ new ClassForIf().publicMethod();
+ }
+}