Test that inlining of static method preserves class initialization

Bug: 71524812
Change-Id: I116e2ac708ade37e4e57d786446cab97268b792e
diff --git a/src/test/examples/staticinlining/Main.java b/src/test/examples/staticinlining/Main.java
new file mode 100644
index 0000000..dfbafa6
--- /dev/null
+++ b/src/test/examples/staticinlining/Main.java
@@ -0,0 +1,26 @@
+// 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 staticinlining;
+
+public class Main {
+  public static String tag = null;
+
+  public static void printMessage(String msg) {
+    System.out.println(msg);
+  }
+
+  public static void test() {
+    tag = "Sub1";
+    Sub1.inlineMe();  // triggers class init of Sub1 (and its hierarchy)
+    tag = "Sub2";
+    Sub2.doNotInlineMe();  // triggers class init of Sub2 (and its hierarchy)
+    printMessage("SuperClass.INIT_TAG=" + SuperClass.INIT_TAG);
+  }
+
+  public static void main(String[] args) {
+    test();
+  }
+
+}
diff --git a/src/test/examples/staticinlining/Sub1.java b/src/test/examples/staticinlining/Sub1.java
new file mode 100644
index 0000000..2e06601
--- /dev/null
+++ b/src/test/examples/staticinlining/Sub1.java
@@ -0,0 +1,16 @@
+// 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 staticinlining;
+
+import inlining.AlwaysInline;
+
+public class Sub1 extends SuperClass {
+
+  @AlwaysInline
+  public static void inlineMe() {
+    Main.printMessage("Sub1");
+  }
+
+}
diff --git a/src/test/examples/staticinlining/Sub2.java b/src/test/examples/staticinlining/Sub2.java
new file mode 100644
index 0000000..e253f79
--- /dev/null
+++ b/src/test/examples/staticinlining/Sub2.java
@@ -0,0 +1,22 @@
+// 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 staticinlining;
+
+public class Sub2 extends SuperClass {
+
+  public static void doNotInlineMe() {
+    System.out.println("Do not inline me 1");
+    System.out.println("Do not inline me 2");
+    System.out.println("Do not inline me 3");
+    System.out.println("Do not inline me 4");
+    System.out.println("Do not inline me 5");
+    System.out.println("Do not inline me 6");
+    System.out.println("Do not inline me 7");
+    System.out.println("Do not inline me 8");
+    System.out.println("Do not inline me 9");
+    System.out.println("Do not inline me 10");
+  }
+
+}
diff --git a/src/test/examples/staticinlining/SuperClass.java b/src/test/examples/staticinlining/SuperClass.java
new file mode 100644
index 0000000..237c11d
--- /dev/null
+++ b/src/test/examples/staticinlining/SuperClass.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 staticinlining;
+
+public class SuperClass {
+  public static final String INIT_TAG = Main.tag;
+}
diff --git a/src/test/examples/staticinlining/keep-rules.txt b/src/test/examples/staticinlining/keep-rules.txt
new file mode 100644
index 0000000..1de091f
--- /dev/null
+++ b/src/test/examples/staticinlining/keep-rules.txt
@@ -0,0 +1,13 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class staticinlining.Main {
+  public static void main(...);
+}
+
+-alwaysinline class * {
+  @inlining.AlwaysInline <methods>;
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8StaticInlining.java b/src/test/java/com/android/tools/r8/ir/optimize/R8StaticInlining.java
new file mode 100644
index 0000000..5102878
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8StaticInlining.java
@@ -0,0 +1,84 @@
+// 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.ir.optimize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.OutputMode;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import org.junit.Assume;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * This test verifies that semantic of class initialization is preserved when a static method
+ * invocation is inlined.
+ */
+@RunWith(Parameterized.class)
+public class R8StaticInlining extends TestBase {
+
+  // TODO(shertz) add CF output
+  @Parameters(name = "{0}_{1}")
+  public static Collection<String[]> data() {
+    return ImmutableList.of(
+        new String[]{"staticinlining", "staticinlining.Main"}
+    );
+  }
+
+  private final String folder;
+  private final String mainClass;
+
+  public R8StaticInlining(String folder, String mainClass) {
+    this.folder = folder;
+    this.mainClass = mainClass;
+  }
+
+  @Test
+  @Ignore("b/71524812")
+  public void testInliningOfStatic() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+
+    Path proguardRules = Paths.get(ToolHelper.EXAMPLES_DIR, folder, "keep-rules.txt");
+    Path jarFile =
+        Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, folder + FileUtils.JAR_EXTENSION);
+
+    // Build with R8
+    AndroidApp.Builder builder = AndroidApp.builder();
+    builder.addProgramFiles(jarFile);
+    AndroidApp app = compileWithR8(builder.build(), proguardRules);
+
+    // Compare original and generated DEX files.
+    String originalDexFile = Paths
+        .get(ToolHelper.EXAMPLES_BUILD_DIR, folder, ToolHelper.DEFAULT_DEX_FILENAME).toString();
+    Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
+    app.writeToZip(generatedDexFile, OutputMode.DexIndexed);
+    String artOutput = ToolHelper
+        .checkArtOutputIdentical(originalDexFile, generatedDexFile.toString(), mainClass,
+            ToolHelper.getDexVm());
+
+    // Compare with Java.
+    ToolHelper.ProcessResult javaResult = ToolHelper.runJava(jarFile, mainClass);
+    if (javaResult.exitCode != 0) {
+      System.out.println(javaResult.stdout);
+      System.err.println(javaResult.stderr);
+      fail("JVM failed for: " + mainClass);
+    }
+    assertEquals("JVM and ART output differ", javaResult.stdout, artOutput);
+  }
+
+}