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);
+ }
+
+}