Simple tests for normalizing parameter type lists
Bug: 195112263
Change-Id: I5e4fd353fd25583d94f6970c8fbdce403e4d71fa
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
similarity index 63%
copy from src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
copy to src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
index c78ced4..d8fa10c 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
@@ -5,16 +5,16 @@
package com.android.tools.r8.optimize.proto;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,7 +22,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class NormalizeTest extends TestBase {
+public class ProtoNormalizationWithKeptMethodTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
@@ -37,6 +37,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addKeepRules("-keep class " + Main.class.getTypeName() + " { void foo(...); }")
.addOptionsModification(
options -> options.testing.enableExperimentalProtoNormalization = true)
.enableInliningAnnotations()
@@ -47,40 +48,50 @@
.compile()
.inspect(
inspector -> {
- ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
-
- ClassSubject bClassSubject = inspector.clazz(B.class);
- assertThat(bClassSubject, isPresent());
+ TypeSubject aTypeSubject = inspector.clazz(A.class).asTypeSubject();
+ TypeSubject bTypeSubject = inspector.clazz(B.class).asTypeSubject();
MethodSubject fooMethodSubject =
inspector.clazz(Main.class).uniqueMethodWithName("foo");
assertThat(fooMethodSubject, isPresent());
+ assertThat(fooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
- String expectedMethodSignature =
- "void "
- + fooMethodSubject.getFinalName()
- + "("
- + aClassSubject.getFinalName()
- + ", "
- + bClassSubject.getFinalName()
- + ")";
- assertEquals(
- expectedMethodSignature,
- fooMethodSubject.getProgramMethod().getMethodSignature().toString());
+ // TODO(b/195112263): Share parameter type lists with Main.foo(B, A) by rewriting to
+ // bar(B, A) and baz(B, A).
+ for (String methodName : new String[] {"bar", "baz"}) {
+ MethodSubject methodSubject =
+ inspector.clazz(Main.class).uniqueMethodWithName(methodName);
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, hasParameters(aTypeSubject, bTypeSubject));
+ }
})
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("A", "B");
+ .assertSuccessWithOutputLines("A", "B", "A", "B", "A", "B");
}
static class Main {
public static void main(String[] args) {
foo(new B(), new A());
+ bar(new B(), new A());
+ baz(new A(), new B());
+ }
+
+ // @Keep
+ @NeverInline
+ static void foo(B b, A a) {
+ System.out.println(a);
+ System.out.println(b);
}
@NeverInline
- static void foo(B b, A a) {
+ static void bar(B b, A a) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @NeverInline
+ static void baz(A a, B b) {
System.out.println(a);
System.out.println(b);
}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java
new file mode 100644
index 0000000..e9655ba
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2022, 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.optimize.proto;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ProtoNormalizationWithKeptVirtualMethodTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepRules("-keepclassmembers class " + A.class.getTypeName() + " { void foo(...); }")
+ .addOptionsModification(
+ options -> options.testing.enableExperimentalProtoNormalization = true)
+ .enableInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ TypeSubject aTypeSubject = aClassSubject.asTypeSubject();
+ TypeSubject bTypeSubject = bClassSubject.asTypeSubject();
+
+ // A.foo(B, A) is kept.
+ MethodSubject fooMethodSubject = aClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooMethodSubject, isPresent());
+ assertThat(fooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
+
+ // B.foo(B, A) overrides kept method.
+ MethodSubject otherFooMethodSubject = bClassSubject.uniqueMethodWithName("foo");
+ assertThat(otherFooMethodSubject, isPresent());
+ assertThat(otherFooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B", "A", "B");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ a.foo(b, a);
+ b.foo(b, a);
+ extra(a, b);
+ }
+
+ // @Keep to ensure that the program has parameter lists (A, B) and (B, A), or we will not
+ // optimize in the first place.
+ static void extra(A a, B b) {}
+ }
+
+ @NoVerticalClassMerging
+ static class A {
+
+ // @Keep
+ @NeverInline
+ public void foo(B b, A a) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @Override
+ public String toString() {
+ return "A";
+ }
+ }
+
+ static class B extends A {
+
+ @NeverInline
+ @Override
+ public void foo(B b, A a) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
new file mode 100644
index 0000000..e4d6bee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2022, 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.optimize.proto;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ProtoNormalizationWithVirtualMethodCollisionTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.testing.enableExperimentalProtoNormalization = true)
+ .enableInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ TypeSubject aTypeSubject = aClassSubject.asTypeSubject();
+ TypeSubject bTypeSubject = bClassSubject.asTypeSubject();
+
+ MethodSubject fooMethodSubject = aClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooMethodSubject, isPresent());
+ assertThat(fooMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+
+ // TODO(b/173398086): Rewriting B.foo(B, A) to B.foo(A, B) would lead to B.foo()
+ // starting to override A.foo(A, B). B.foo(B, A) could either be rewritten to
+ // B.foo$1(A, B) if B.foo(B, A) is not related by overriding to a kept method, or an
+ // extra unused argument could be appended.
+ MethodSubject otherFooMethodSubject = bClassSubject.uniqueMethodWithName("foo");
+ assertThat(otherFooMethodSubject, isPresent());
+ assertThat(otherFooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B", "A", "B");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ a.foo(a, b);
+ b.foo(b, a);
+ }
+ }
+
+ @NoVerticalClassMerging
+ static class A {
+
+ @NeverInline
+ public void foo(A a, B b) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @Override
+ public String toString() {
+ return "A";
+ }
+ }
+
+ static class B extends A {
+
+ @NeverInline
+ public void foo(B b, A a) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
similarity index 73%
rename from src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
rename to src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
index c78ced4..e468af8 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
@@ -5,16 +5,16 @@
package com.android.tools.r8.optimize.proto;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,7 +22,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class NormalizeTest extends TestBase {
+public class ProtoNormalizationWithoutSharingTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
@@ -47,27 +47,14 @@
.compile()
.inspect(
inspector -> {
- ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
+ TypeSubject aTypeSubject = inspector.clazz(A.class).asTypeSubject();
+ TypeSubject bTypeSubject = inspector.clazz(B.class).asTypeSubject();
- ClassSubject bClassSubject = inspector.clazz(B.class);
- assertThat(bClassSubject, isPresent());
-
+ // TODO(b/173398086): Should not be normalized as there is no sharing of protos.
MethodSubject fooMethodSubject =
inspector.clazz(Main.class).uniqueMethodWithName("foo");
assertThat(fooMethodSubject, isPresent());
-
- String expectedMethodSignature =
- "void "
- + fooMethodSubject.getFinalName()
- + "("
- + aClassSubject.getFinalName()
- + ", "
- + bClassSubject.getFinalName()
- + ")";
- assertEquals(
- expectedMethodSignature,
- fooMethodSubject.getProgramMethod().getMethodSignature().toString());
+ assertThat(fooMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A", "B");
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 4ffcdb4..9be6468 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -77,6 +77,16 @@
}
@Override
+ public TypeSubject getParameter(int index) {
+ throw new Unreachable("Cannot get the parameter for an absent method");
+ }
+
+ @Override
+ public List<TypeSubject> getParameters() {
+ throw new Unreachable("Cannot get the parameters for an absent method");
+ }
+
+ @Override
public ProgramMethod getProgramMethod() {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 04f0d52..30b3fb0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -171,6 +171,10 @@
return null;
}
+ public TypeSubject asTypeSubject() {
+ return null;
+ }
+
@Override
public abstract ClassAccessFlags getAccessFlags();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 0d62343..cdc420e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -289,6 +289,11 @@
}
@Override
+ public TypeSubject asTypeSubject() {
+ return new TypeSubject(codeInspector, dexClass.getType());
+ }
+
+ @Override
public boolean isAbstract() {
return dexClass.accessFlags.isAbstract();
}
@@ -504,10 +509,6 @@
return dexClass.toSourceString();
}
- public TypeSubject asTypeSubject() {
- return new TypeSubject(codeInspector, getDexProgramClass().type);
- }
-
@Override
public KmClassSubject getKmClass() {
AnnotationSubject annotationSubject = annotation(METADATA_TYPE);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index a41ab17..b99b090 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -124,6 +124,18 @@
}
@Override
+ public TypeSubject getParameter(int index) {
+ return new TypeSubject(codeInspector, getMethod().getParameter(index));
+ }
+
+ @Override
+ public List<TypeSubject> getParameters() {
+ return getMethod().getParameters().stream()
+ .map(parameter -> new TypeSubject(codeInspector, parameter))
+ .collect(Collectors.toList());
+ }
+
+ @Override
public ProgramMethod getProgramMethod() {
return new ProgramMethod(clazz.getDexProgramClass(), getMethod());
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodMatchers.java
new file mode 100644
index 0000000..8d7e78b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodMatchers.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2022, 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.utils.codeinspector;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class MethodMatchers {
+
+ public static Matcher<MethodSubject> hasParameters(TypeSubject... expectedParameters) {
+ return hasParameters(Arrays.asList(expectedParameters));
+ }
+
+ public static Matcher<MethodSubject> hasParameters(List<TypeSubject> expectedParameters) {
+ return new TypeSafeMatcher<MethodSubject>() {
+ @Override
+ protected boolean matchesSafely(MethodSubject methodSubject) {
+ if (!methodSubject.isPresent()) {
+ return false;
+ }
+ if (methodSubject.getParameters().size() != expectedParameters.size()) {
+ return false;
+ }
+ for (int i = 0; i < expectedParameters.size(); i++) {
+ TypeSubject actualParameter = methodSubject.getParameter(i);
+ TypeSubject expectedParameter = expectedParameters.get(i);
+ assertEquals(expectedParameter, actualParameter);
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(
+ "has parameters ("
+ + StringUtils.join(", ", expectedParameters, TypeSubject::getTypeName)
+ + ")");
+ }
+
+ @Override
+ public void describeMismatchSafely(final MethodSubject subject, Description description) {
+ description.appendText("method did not");
+ }
+ };
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 594b17a..3b7fe05 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.google.common.collect.Streams;
import java.util.Iterator;
+import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -51,6 +52,10 @@
public abstract DexEncodedMethod getMethod();
+ public abstract TypeSubject getParameter(int index);
+
+ public abstract List<TypeSubject> getParameters();
+
public abstract ProgramMethod getProgramMethod();
public Iterator<InstructionSubject> iterateInstructions() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
index 357c777..5761b99 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2022, 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.
@@ -17,6 +17,10 @@
this.dexType = dexType;
}
+ public String getTypeName() {
+ return dexType.getTypeName();
+ }
+
@Override
public boolean isPresent() {
return true;