Merge "Add test for invoke-special instructions"
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index c098fb8..d5d0558 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -807,7 +807,9 @@
public static R8Command.Builder prepareR8CommandBuilder(
AndroidApp app, ProgramConsumer programConsumer) {
- return R8Command.builder(app).setProgramConsumer(programConsumer);
+ return R8Command.builder(app)
+ .setProgramConsumer(programConsumer)
+ .setProguardMapConsumer(StringConsumer.emptyConsumer());
}
public static AndroidApp runR8(AndroidApp app) throws IOException {
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 e29ceae..10c64e3 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
@@ -20,10 +20,12 @@
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
+import java.util.function.Consumer;
public class ProguardCompatabilityTestBase extends TestBase {
@@ -87,11 +89,18 @@
throw new IllegalArgumentException("Unknown shrinker: " + mode);
}
- protected AndroidApp runR8Raw(List<Class> programClasses, String proguardConfig) throws Exception {
+ protected AndroidApp runR8Raw(List<Class> programClasses, String proguardConfig)
+ throws Exception {
+ return runR8Raw(programClasses, proguardConfig, null);
+ }
+
+ protected AndroidApp runR8Raw(
+ List<Class> programClasses, String proguardConfig, Consumer<InternalOptions> configure)
+ throws Exception {
AndroidApp app = readClassesAndAndriodJar(programClasses);
R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app);
builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
- return ToolHelper.runR8(builder.build());
+ return ToolHelper.runR8(builder.build(), configure);
}
protected DexInspector runR8(List<Class> programClasses, String proguardConfig) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
new file mode 100644
index 0000000..14391ee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
@@ -0,0 +1,105 @@
+// 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.inlining;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+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.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;
+
+class A {
+ static public int a() {
+ try {
+ String p = A.class.getPackage().getName();
+ Class.forName(p + ".D");
+ } catch (ClassNotFoundException e) {
+ return 2;
+ }
+ return 1;
+ }
+}
+
+class B {
+ // Depending on inlining option, this method is kept to make inlining of A.a() infeasible.
+ void x() {
+ System.out.print("" + A.a() + A.a() + A.a() + A.a() + A.a() + A.a() + A.a() + A.a());
+ }
+}
+
+class D {
+}
+
+class Main {
+ public static void main(String[] args) {
+ System.out.print("" + A.a());
+ }
+}
+
+@RunWith(Parameterized.class)
+public class IfRuleWithInlining extends ProguardCompatabilityTestBase {
+ private final static List<Class> CLASSES = ImmutableList.of(
+ A.class, B.class, D.class, Main.class);
+
+ private final Shrinker shrinker;
+ private final boolean inlineMethod;
+
+ public IfRuleWithInlining(Shrinker shrinker, boolean inlineMethod) {
+ this.shrinker = shrinker;
+ this.inlineMethod = inlineMethod;
+ }
+
+ @Parameters(name = "shrinker: {0} inlineMethod: {1}")
+ public static Collection<Object[]> data() {
+ // We don't run this on Proguard, as triggering inlining in Proguard is out of our control.
+ return ImmutableList.of(
+ new Object[]{Shrinker.R8, true},
+ new Object[]{Shrinker.R8, false}
+ );
+ }
+
+ private void check(AndroidApp app) throws Exception {
+ DexInspector inspector = new DexInspector(app);
+ ClassSubject clazzA = inspector.clazz(A.class);
+ assertThat(clazzA, isPresent());
+ // A.a might be inlined.
+ assertEquals(!inlineMethod, clazzA.method("int", "a", ImmutableList.of()).isPresent());
+ // TODO(110148109): class D should be present - inlining or not.
+ assertEquals(!inlineMethod, inspector.clazz(D.class).isPresent());
+ ProcessResult result = runOnArtRaw(app, Main.class.getName());
+ assertEquals(0, result.exitCode);
+ // TODO(110148109): Output should be the same - inlining or not.
+ assertEquals(!inlineMethod ? "1" : "2", result.stdout);
+ }
+
+ @Test
+ public void testMergedClassMethodInIfRule() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-keep class **.Main { public static void main(java.lang.String[]); }",
+ inlineMethod
+ ? "-alwaysinline class **.A { int a(); }"
+ : "-keep class **.B { *; }",
+ inlineMethod
+ ? "-checkdiscard class **.A { int a(); }"
+ : "",
+ "-if class **.A { static int a(); }",
+ "-keep class **.D",
+ "-dontobfuscate"
+ );
+
+ check(runShrinkerRaw(shrinker, CLASSES, config));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
new file mode 100644
index 0000000..b484d73
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
@@ -0,0 +1,156 @@
+// 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.verticalclassmerging;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+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.InternalOptions;
+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;
+
+class A {
+ int x = 1;
+ int a() throws ClassNotFoundException {
+ // Class D is expected to be kept - vertical class merging or not. The -if rule say that if
+ // the method A.a is in the output, then class D is needed.
+ String p =getClass().getPackage().getName();
+ Class.forName(p + ".D");
+ return 4;
+ }
+}
+
+class B extends A {
+ int y = 2;
+ int b() {
+ return 5;
+ }
+}
+
+class C extends B {
+ int z = 3;
+ int c() {
+ return 6;
+ }
+}
+
+class D {
+}
+
+class Main {
+ public static void main(String[] args) throws ClassNotFoundException {
+ C c = new C();
+ System.out.print("" + c.x + "" + c.y + "" + c.z + "" + c.a() + "" + c.b() + "" + c.c());
+ }
+}
+
+@RunWith(Parameterized.class)
+public class IfRuleWithVerticalClassMerging extends ProguardCompatabilityTestBase {
+ private final static List<Class> CLASSES = ImmutableList.of(
+ A.class, B.class, C.class, D.class, Main.class);
+
+ private final Shrinker shrinker;
+ private final boolean enableClassMerging;
+
+ public IfRuleWithVerticalClassMerging(Shrinker shrinker, boolean enableClassMerging) {
+ this.shrinker = shrinker;
+ this.enableClassMerging = enableClassMerging;
+ }
+
+ @Parameters(name = "shrinker: {0} classMerging: {1}")
+ public static Collection<Object[]> data() {
+ // We don't run this on Proguard, as Proguard does not merge A into B.
+ return ImmutableList.of(
+ new Object[]{Shrinker.R8, true},
+ new Object[]{Shrinker.R8, false}
+ );
+ }
+
+ private void configure(InternalOptions options) {
+ options.enableClassMerging = enableClassMerging;
+ }
+
+ @Override
+ protected AndroidApp runR8Raw(
+ List<Class> programClasses, String proguardConfig) throws Exception {
+ return super.runR8Raw(programClasses, proguardConfig, this::configure);
+ }
+
+ private void check(AndroidApp app) throws Exception {
+ DexInspector inspector = new DexInspector(app);
+ ClassSubject clazzA = inspector.clazz(A.class);
+ assertEquals(!enableClassMerging, clazzA.isPresent());
+ ClassSubject clazzB = inspector.clazz(B.class);
+ assertThat(clazzB, isPresent());
+ ClassSubject clazzD = inspector.clazz(D.class);
+ // TODO(110141157): Class D should be kept - vertical class merging or not.
+ assertEquals(!enableClassMerging, clazzD.isPresent());
+
+ ProcessResult result = runOnArtRaw(app, Main.class.getName());
+ // TODO(110141157): The code should run - vertical class merging or not.
+ assertEquals(enableClassMerging ? 1 : 0, result.exitCode);
+ if (!enableClassMerging) {
+ assertEquals("123456", result.stdout);
+ }
+ }
+
+ @Test
+ public void testMergedClassInIfRule() throws Exception {
+ // Class C is kept, meaning that it will not be touched.
+ // Class A will be merged into class B.
+ List<String> config = ImmutableList.of(
+ "-keep class **.Main { public static void main(java.lang.String[]); }",
+ "-keep class **.C",
+ "-if class **.A",
+ "-keep class **.D",
+ "-dontobfuscate"
+ );
+
+ check(runShrinkerRaw(shrinker, CLASSES, config));
+ }
+
+ @Test
+ public void testMergedClassFieldInIfRule() throws Exception {
+ // Class C is kept, meaning that it will not be touched.
+ // Class A will be merged into class B.
+ // Main.main access A.x, so that field exists satisfying the if rule.
+ List<String> config = ImmutableList.of(
+ "-keep class **.Main { public static void main(java.lang.String[]); }",
+ "-keep class **.C",
+ "-if class **.A { int x; }",
+ "-keep class **.D",
+ "-dontobfuscate"
+ );
+
+ check(runShrinkerRaw(shrinker, CLASSES, config));
+ }
+
+ @Test
+ public void testMergedClassMethodInIfRule() throws Exception {
+ // Class C is kept, meaning that it will not be touched.
+ // Class A will be merged into class B.
+ // Main.main access A.a(), that method exists satisfying the if rule.
+ List<String> config = ImmutableList.of(
+ "-keep class **.Main { public static void main(java.lang.String[]); }",
+ "-keep class **.C",
+ "-if class **.A { int a(); }",
+ "-keep class **.D",
+ "-dontobfuscate"
+ );
+
+ check(runShrinkerRaw(shrinker, CLASSES, config));
+ }
+}