Introduce tests about option -overloadaggressively.

Bug: 72858955
Change-Id: I463ee2532aaab68adc4b25e487d00fb0847b8656
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index 50b95ea..b2560ff 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -5,12 +5,14 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.origin.EmbeddedOrigin;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.List;
 
 public class CompatProguardCommandBuilder extends R8Command.Builder {
-  private static final List<String> REFLECTIONS = ImmutableList.of(
+  @VisibleForTesting
+  public static final List<String> REFLECTIONS = ImmutableList.of(
       "-identifiernamestring public class java.lang.Class {",
       "  public static java.lang.Class forName(java.lang.String);",
       "  public java.lang.reflect.Field getField(java.lang.String);",
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index c451402..b1b572f 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -10,20 +10,12 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
 import org.junit.Assert;
 
 public class AsmTestBase extends TestBase {
@@ -104,15 +96,6 @@
     ensureSameOutput(main, mergedApp, classes);
   }
 
-  protected AndroidApp buildAndroidApp(byte[]... classes) throws IOException {
-    AndroidApp.Builder builder = AndroidApp.builder();
-    for (byte[] clazz : classes) {
-      builder.addClassProgramData(clazz, Origin.unknown());
-    }
-    builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.N.getLevel()));
-    return builder.build();
-  }
-
   protected static AndroidApp readClassesAndAsmDump(List<Class> classes, List<byte[]> asmClasses)
       throws IOException {
     AndroidApp.Builder builder = AndroidApp.builder();
@@ -130,32 +113,6 @@
     assertTrue(result.stderr, result.stderr.contains(exception.getCanonicalName()));
   }
 
-  protected ProcessResult runOnJava(String main, byte[]... classes) throws IOException {
-    Path file = writeToZip(classes);
-    return ToolHelper.runJavaNoVerify(file, main);
-  }
-
-  private Path writeToZip(byte[]... classes) throws IOException {
-    DumpLoader dumpLoader = new DumpLoader();
-    File result = temp.newFile();
-    try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(result.toPath(),
-        StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
-      for (byte[] clazz : classes) {
-        String name = dumpLoader.loadClass(clazz).getTypeName();
-        ZipEntry zipEntry = new ZipEntry(DescriptorUtils.getPathFromJavaType(name));
-        zipEntry.setSize(clazz.length);
-        out.putNextEntry(zipEntry);
-        out.write(clazz);
-        out.closeEntry();
-      }
-    }
-    return result.toPath();
-  }
-
-  protected static Class loadClassFromDump(byte[] dump) {
-    return new DumpLoader().loadClass(dump);
-  }
-
   @FunctionalInterface
   protected interface AsmDump {
 
@@ -164,7 +121,7 @@
 
   protected static Class loadClassFromAsmClass(AsmDump asmDump) {
     try {
-      return new DumpLoader().loadClass(asmDump.dump());
+      return loadClassFromDump(asmDump.dump());
     } catch (Exception e) {
       throw new ClassFormatError(e.toString());
     }
@@ -177,13 +134,4 @@
       throw new ClassFormatError(e.toString());
     }
   }
-
-  private static class DumpLoader extends ClassLoader {
-
-    @SuppressWarnings("deprecation")
-    public Class loadClass(byte[] clazz) {
-      return defineClass(clazz, 0, clazz.length);
-    }
-  }
-
 }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index f02aa2b..500b55c 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.graph.DexCode;
@@ -33,6 +34,7 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -44,6 +46,7 @@
 import java.util.jar.JarOutputStream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
 
@@ -62,6 +65,18 @@
   }
 
   /**
+   * Build an AndroidApp with the specified test classes as byte array.
+   */
+  protected AndroidApp buildAndroidApp(byte[]... classes) throws IOException {
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (byte[] clazz : classes) {
+      builder.addClassProgramData(clazz, Origin.unknown());
+    }
+    builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.N.getLevel()));
+    return builder.build();
+  }
+
+  /**
    * Build an AndroidApp with the specified test classes.
    */
   protected static AndroidApp readClasses(Class... classes) throws IOException {
@@ -367,12 +382,20 @@
   }
 
   /**
+   * Run application on the specified version of Art with the specified main class.
+   */
+  protected ProcessResult runOnArtRaw(AndroidApp app, String mainClass, DexVm version)
+      throws IOException {
+    Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
+    app.writeToZip(out, OutputMode.DexIndexed);
+    return ToolHelper.runArtRaw(ImmutableList.of(out.toString()), mainClass, null, version);
+  }
+
+  /**
    * Run application on Art with the specified main class.
    */
   protected ProcessResult runOnArtRaw(AndroidApp app, String mainClass) throws IOException {
-    Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
-    app.writeToZip(out, OutputMode.DexIndexed);
-    return ToolHelper.runArtRaw(ImmutableList.of(out.toString()), mainClass, null);
+    return runOnArtRaw(app, mainClass, null);
   }
 
   /**
@@ -406,7 +429,17 @@
   /**
    * Run application on Art with the specified main class and provided arguments.
    */
-  protected String runOnArt(AndroidApp app, String mainClass, List<String> args) throws IOException {
+  protected String runOnArt(AndroidApp app, String mainClass, List<String> args)
+      throws IOException {
+    return runOnArt(app, mainClass, args, null);
+  }
+
+  /**
+   * Run application on Art with the specified main class, provided arguments, and specified VM
+   * version.
+   */
+  protected String runOnArt(AndroidApp app, String mainClass, List<String> args, DexVm dexVm)
+      throws IOException {
     Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
     app.writeToZip(out, OutputMode.DexIndexed);
     return ToolHelper.runArtNoVerificationErrors(
@@ -416,7 +449,8 @@
           for (String arg : args) {
             builder.appendProgramArgument(arg);
           }
-        });
+        },
+        dexVm);
   }
 
   /**
@@ -448,6 +482,38 @@
     return result.stdout;
   }
 
+  protected ProcessResult runOnJava(String main, byte[]... classes) throws IOException {
+    Path file = writeToZip(classes);
+    return ToolHelper.runJavaNoVerify(file, main);
+  }
+
+  private Path writeToZip(byte[]... classes) throws IOException {
+    File result = temp.newFile();
+    try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(result.toPath(),
+        StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
+      for (byte[] clazz : classes) {
+        String name = loadClassFromDump(clazz).getTypeName();
+        ZipEntry zipEntry = new ZipEntry(DescriptorUtils.getPathFromJavaType(name));
+        zipEntry.setSize(clazz.length);
+        out.putNextEntry(zipEntry);
+        out.write(clazz);
+        out.closeEntry();
+      }
+    }
+    return result.toPath();
+  }
+
+  protected static Class loadClassFromDump(byte[] dump) {
+    return new DumpLoader().loadClass(dump);
+  }
+
+  private static class DumpLoader extends ClassLoader {
+
+    @SuppressWarnings("deprecation")
+    public Class loadClass(byte[] clazz) {
+      return defineClass(clazz, 0, clazz.length);
+    }
+  }
 
   /**
    * Disassemble the content of an application. Only works for an application with only dex code.
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 0cc7c93..62a9c49 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -7,7 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
@@ -41,7 +41,7 @@
 import java.util.function.BiConsumer;
 import org.junit.Test;
 
-public class NullabilityTest extends AsmTestBase {
+public class NullabilityTest extends TestBase {
   private static final InternalOptions TEST_OPTIONS = new InternalOptions();
 
   private void buildAndTest(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java
index f6c5fbf..b139f32 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java
@@ -7,7 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
@@ -31,7 +31,7 @@
 import java.util.function.Consumer;
 import org.junit.Test;
 
-public class NonNullMarkerTest extends AsmTestBase {
+public class NonNullMarkerTest extends TestBase {
   private static final InternalOptions TEST_OPTIONS = new InternalOptions();
 
   private void buildAndTest(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
index b8105db..4fc51b5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
@@ -5,7 +5,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.Format21t;
 import com.android.tools.r8.code.Format22t;
@@ -23,7 +23,7 @@
 import java.util.List;
 import org.junit.Test;
 
-public class SimplifyIfNotNullTest extends AsmTestBase {
+public class SimplifyIfNotNullTest extends TestBase {
   private static boolean isIf(Instruction instruction) {
     return instruction instanceof Format21t || instruction instanceof Format22t;
   }
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/A.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/A.java
new file mode 100644
index 0000000..af4c4b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/A.java
@@ -0,0 +1,10 @@
+// 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.naming.overloadaggressively;
+
+public class A {
+  volatile int f1;
+  volatile Object f2;
+  volatile B f3;
+}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
new file mode 100644
index 0000000..78f5a24
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
@@ -0,0 +1,9 @@
+// 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.naming.overloadaggressively;
+
+public class B {
+  volatile int f1;
+  volatile Object f2;
+}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/Main.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/Main.java
new file mode 100644
index 0000000..956b198
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/Main.java
@@ -0,0 +1,37 @@
+// 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.naming.overloadaggressively;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+public class Main {
+  @SuppressWarnings("unchecked")
+  public static void main(String[] args) throws Exception {
+    A a = new A();
+    B b = new B();
+    AtomicReferenceFieldUpdater f3Updater =
+        AtomicReferenceFieldUpdater.newUpdater(A.class, B.class, "f3");
+    f3Updater.set(a, b);
+    AtomicReferenceFieldUpdater f2Updater =
+        AtomicReferenceFieldUpdater.newUpdater(A.class, Object.class, "f2");
+    f2Updater.set(a, b);
+    assert a.f2 instanceof B;
+    assert a.f2 == a.f3;
+    ((B) a.f2).f2 = a;
+    assert b.f2 instanceof A;
+    assert ((A) b.f2).f2 == b;
+
+    Random random = new Random();
+    int next = random.nextInt();
+    AtomicIntegerFieldUpdater f1Updater =
+        AtomicIntegerFieldUpdater.newUpdater(A.class, "f1");
+    f1Updater.set(a, next);
+    B viaF3 = a.f3;
+    viaF3.f1 = next;
+    int diff = viaF3.f1 - a.f1;
+    System.out.println("diff: " + diff);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
new file mode 100644
index 0000000..d0cdb28
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -0,0 +1,112 @@
+// 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.naming.overloadaggressively;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.CompatProguardCommandBuilder;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.DexVm.Kind;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.origin.Origin;
+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 com.google.common.collect.Iterables;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assume;
+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 OverloadAggressivelyTest extends TestBase {
+  private final DexVm dexVm;
+  private final boolean overloadaggressively;
+
+  public OverloadAggressivelyTest(DexVm dexVm, boolean overloadaggressively) {
+    this.dexVm = dexVm;
+    this.overloadaggressively = overloadaggressively;
+  }
+
+  @Parameters(name = "vm: {0}, overloadaggressively: {1}")
+  public static Collection<Object[]> data() {
+    List<Object[]> testCases = new ArrayList<>();
+    for (DexVm version : DexVm.values()) {
+      if (version.getKind() == Kind.HOST) {
+        testCases.add(new Object[]{version, true});
+        testCases.add(new Object[]{version, false});
+      }
+    }
+    return testCases;
+  }
+
+  @Test
+  public void overloadAggressivelyTest() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    byte[][] classes = {
+        ToolHelper.getClassAsBytes(Main.class),
+        ToolHelper.getClassAsBytes(A.class),
+        ToolHelper.getClassAsBytes(B.class)
+    };
+    AndroidApp originalApp = buildAndroidApp(classes);
+    Path out = temp.getRoot().toPath();
+    R8Command command =
+        ToolHelper.addProguardConfigurationConsumer(
+            ToolHelper.prepareR8CommandBuilder(originalApp),
+            pgConfig -> {
+              pgConfig.setPrintMapping(true);
+              pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+            })
+        .addProguardConfiguration(
+            ImmutableList.copyOf(Iterables.concat(ImmutableList.of(
+                keepMainProguardConfiguration(Main.class),
+                overloadaggressively ? "-overloadaggressively" : ""),
+                CompatProguardCommandBuilder.REFLECTIONS)),
+            Origin.unknown())
+        .setOutput(out, OutputMode.DexIndexed)
+        .build();
+    AndroidApp processedApp = ToolHelper.runR8(command);
+
+    DexInspector dexInspector = new DexInspector(
+        out.resolve(ToolHelper.DEFAULT_DEX_FILENAME),
+        out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE).toString());
+    ClassSubject a = dexInspector.clazz(A.class.getCanonicalName());
+    DexEncodedField f1 = a.field("int", "f1").getField();
+    assertNotNull(f1);
+    DexEncodedField f2 = a.field("java.lang.Object", "f2").getField();
+    assertNotNull(f2);
+    assertEquals(overloadaggressively, f1.field.name == f2.field.name);
+    DexEncodedField f3 = a.field(B.class.getCanonicalName(), "f3").getField();
+    assertNotNull(f3);
+    assertEquals(overloadaggressively, f1.field.name == f3.field.name);
+    assertEquals(overloadaggressively, f2.field.name == f3.field.name);
+
+    ProcessResult javaOutput = runOnJava(Main.class.getCanonicalName(), classes);
+    ProcessResult artOutput = runOnArtRaw(processedApp, Main.class.getCanonicalName(), dexVm);
+    if (overloadaggressively) {
+      assertNotEquals(0, artOutput.exitCode);
+      assertTrue(artOutput.stderr.contains("ClassCastException"));
+    } else {
+      assertEquals(0, javaOutput.exitCode);
+      assertEquals(0, artOutput.exitCode);
+      assertEquals(javaOutput.stdout.trim(), artOutput.stdout.trim());
+      // ART may dump its own debugging info through stderr.
+      // assertEquals(javaOutput.stderr.trim(), artOutput.stderr.trim());
+    }
+  }
+}