Add a test for non-rebound fields with horizontal class merging
Change-Id: I204e4bd1dc29187c9a1de8becd00ba4826d484ce
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 0d6b606..a7bd6c1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -461,6 +461,7 @@
public void setHorizontallyMergedClasses(HorizontallyMergedClasses horizontallyMergedClasses) {
assert this.horizontallyMergedClasses == null;
this.horizontallyMergedClasses = horizontallyMergedClasses;
+ testing().horizontallyMergedClassesConsumer.accept(dexItemFactory(), horizontallyMergedClasses);
}
/**
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index ef58b3a..b1d0dbe 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -96,6 +96,7 @@
Collection<Collection<DexProgramClass>> groups = policyExecutor.run(classes.values());
// If there are no groups, then end horizontal class merging.
if (groups.isEmpty()) {
+ appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
return null;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 01058e2..653e31a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -15,12 +15,17 @@
import java.util.Set;
public class HorizontallyMergedClasses implements MergedClasses {
+
private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
public HorizontallyMergedClasses(BidirectionalManyToOneMap<DexType, DexType> mergedClasses) {
this.mergedClasses = mergedClasses;
}
+ public static HorizontallyMergedClasses empty() {
+ return new HorizontallyMergedClasses(new BidirectionalManyToOneMap<>());
+ }
+
public DexType getMergeTargetOrDefault(DexType type) {
return mergedClasses.getOrDefault(type, type);
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 60153de..8bc1a55 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
@@ -1230,6 +1231,9 @@
new DefaultRepackagingConfiguration(
appView.dexItemFactory(), appView.options().getProguardConfiguration());
+ public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
+ ConsumerUtils.emptyBiConsumer();
+
public BiConsumer<DexItemFactory, HorizontallyMergedLambdaClasses>
horizontallyMergedLambdaClassesConsumer = ConsumerUtils.emptyBiConsumer();
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
new file mode 100644
index 0000000..91dc3b1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.testclasses.NonReboundFieldAccessOnMergedClassTestClasses;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonReboundFieldAccessOnMergedClassTest extends HorizontalClassMergingTestBase {
+
+ public NonReboundFieldAccessOnMergedClassTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (enableHorizontalClassMerging) {
+ thrown.expect(Throwable.class);
+ }
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addHorizontallyMergedLambdaClassesInspector(
+ inspector -> {
+ if (enableHorizontalClassMerging) {
+ inspector.assertMerged(C.class, D.class);
+ }
+ })
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.print(new C("Hello").greeting);
+ System.out.println(new D(" world!").greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class C extends NonReboundFieldAccessOnMergedClassTestClasses.B {
+
+ public C(String greeting) {
+ super(greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class D extends NonReboundFieldAccessOnMergedClassTestClasses.B {
+
+ public D(String greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
new file mode 100644
index 0000000..627c2d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2020, 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.NonReboundFieldAccessOnMergedClassTest.C;
+import com.android.tools.r8.classmerging.horizontal.testclasses.NonReboundFieldAccessWithMergedTypeTestClasses;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonReboundFieldAccessWithMergedTypeTest extends HorizontalClassMergingTestBase {
+
+ public NonReboundFieldAccessWithMergedTypeTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (enableHorizontalClassMerging) {
+ thrown.expect(Throwable.class);
+ }
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addHorizontallyMergedLambdaClassesInspector(
+ inspector -> {
+ if (enableHorizontalClassMerging) {
+ inspector.assertMerged(HelloGreeting.class, WorldGreeting.class);
+ }
+ })
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.print(new C(new HelloGreeting()).greeting);
+ System.out.println(new WorldGreeting());
+ }
+ }
+
+ @NeverClassInline
+ static class C extends NonReboundFieldAccessWithMergedTypeTestClasses.B {
+
+ public C(HelloGreeting greeting) {
+ super(greeting);
+ }
+ }
+
+ public static class HelloGreeting {
+
+ @Override
+ public String toString() {
+ return "Hello";
+ }
+ }
+
+ public static class WorldGreeting {
+
+ @Override
+ public String toString() {
+ return " world!";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
new file mode 100644
index 0000000..7bf88cf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, 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.classmerging.horizontal.testclasses;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+
+public class NonReboundFieldAccessOnMergedClassTestClasses {
+
+ @NoVerticalClassMerging
+ static class A {
+
+ public String greeting;
+
+ A(String greeting) {
+ this.greeting = greeting;
+ }
+ }
+
+ public static class B extends A {
+
+ public B(String greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
new file mode 100644
index 0000000..96e80b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2020, 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.classmerging.horizontal.testclasses;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.classmerging.horizontal.NonReboundFieldAccessWithMergedTypeTest.HelloGreeting;
+
+public class NonReboundFieldAccessWithMergedTypeTestClasses {
+
+ @NoHorizontalClassMerging
+ @NoVerticalClassMerging
+ static class A {
+
+ public HelloGreeting greeting;
+
+ A(HelloGreeting greeting) {
+ this.greeting = greeting;
+ }
+ }
+
+ @NoVerticalClassMerging
+ public static class B extends A {
+
+ public B(HelloGreeting greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
new file mode 100644
index 0000000..80e8a5c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, 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 com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+
+public class HorizontallyMergedClassesInspector {
+
+ private final DexItemFactory dexItemFactory;
+ private final HorizontallyMergedClasses horizontallyMergedClasses;
+
+ public HorizontallyMergedClassesInspector(
+ DexItemFactory dexItemFactory, HorizontallyMergedClasses horizontallyMergedClasses) {
+ this.dexItemFactory = dexItemFactory;
+ this.horizontallyMergedClasses = horizontallyMergedClasses;
+ }
+
+ public HorizontallyMergedClassesInspector assertMerged(Class<?> clazz) {
+ assertTrue(horizontallyMergedClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertMerged(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ assertMerged(clazz);
+ }
+ return this;
+ }
+}