Tests for handle missing fields in static initializer rewriting

Tests for the fix <to insert hash when fix is committed>

Bug: 62524153
Change-Id: Ie8d15cc254bb0d3a461f18b8dcdeb458268a3e54
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e46ff48..89a7a26 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -101,6 +101,15 @@
   }
 
   /**
+   * Compile an application with D8.
+   */
+  protected AndroidApp compileWithD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+      throws CompilationException, ExecutionException, IOException {
+    D8Command command = ToolHelper.prepareD8CommandBuilder(app).build();
+    return ToolHelper.runD8(command, optionsConsumer);
+  }
+
+  /**
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(Class... classes)
@@ -249,11 +258,25 @@
   /**
    * Run application on Art with the specified main class.
    */
-  protected String runOnArt(AndroidApp app, Class mainClass) throws IOException {
+  protected ProcessResult runOnArtRaw(AndroidApp app, String mainClass) throws IOException {
     Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
     app.writeToZip(out, OutputMode.Indexed);
-    return ToolHelper.runArtNoVerificationErrors(
-        ImmutableList.of(out.toString()), mainClass.getCanonicalName(), null);
+    return ToolHelper.runArtNoVerificationErrorsRaw(
+        ImmutableList.of(out.toString()), mainClass, null);
+  }
+
+  /**
+   * Run application on Art with the specified main class.
+   */
+  protected ProcessResult runOnArtRaw(AndroidApp app, Class mainClass) throws IOException {
+    return runOnArtRaw(app, mainClass.getCanonicalName());
+  }
+
+  /**
+   * Run application on Art with the specified main class.
+   */
+  protected String runOnArt(AndroidApp app, Class mainClass) throws IOException {
+    return runOnArtRaw(app, mainClass).stdout;
   }
 
   /**
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 79be8a8..d435ae5 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -773,18 +772,29 @@
     return Paths.get(System.getProperty("java.home"), "bin", "java").toString();
   }
 
+  public static ProcessResult runArtNoVerificationErrorsRaw(String file, String mainClass)
+      throws IOException {
+    return runArtNoVerificationErrorsRaw(Collections.singletonList(file), mainClass, null);
+  }
+
   public static String runArtNoVerificationErrors(String file, String mainClass)
       throws IOException {
-    return runArtNoVerificationErrors(Collections.singletonList(file), mainClass, null);
+    return runArtNoVerificationErrorsRaw(file, mainClass).stdout;
+  }
+
+  public static ProcessResult runArtNoVerificationErrorsRaw(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras)
+      throws IOException {
+    return runArtNoVerificationErrorsRaw(files, mainClass, extras, null);
   }
 
   public static String runArtNoVerificationErrors(List<String> files, String mainClass,
       Consumer<ArtCommandBuilder> extras)
       throws IOException {
-    return runArtNoVerificationErrors(files, mainClass, extras, null);
+    return runArtNoVerificationErrorsRaw(files, mainClass, extras).stdout;
   }
 
-  public static String runArtNoVerificationErrors(List<String> files, String mainClass,
+  public static ProcessResult runArtNoVerificationErrorsRaw(List<String> files, String mainClass,
       Consumer<ArtCommandBuilder> extras,
       DexVm version)
       throws IOException {
@@ -795,18 +805,30 @@
     if (extras != null) {
       extras.accept(builder);
     }
-    return runArtNoVerificationErrors(builder);
+    return runArtNoVerificationErrorsRaw(builder);
   }
 
-  public static String runArtNoVerificationErrors(ArtCommandBuilder builder) throws IOException {
-    ProcessResult result = runArtProcess(builder);
+  public static String runArtNoVerificationErrors(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras,
+      DexVm version)
+      throws IOException {
+    return runArtNoVerificationErrorsRaw(files, mainClass, extras, version).stdout;
+  }
+
+  public static ProcessResult runArtNoVerificationErrorsRaw(ArtCommandBuilder builder)
+      throws IOException {
+    ProcessResult result = runArtProcessRaw(builder);
     if (result.stderr.contains("Verification error")) {
       fail("Verification error: \n" + result.stderr);
     }
-    return result.stdout;
+    return result;
   }
 
-  private static ProcessResult runArtProcess(ArtCommandBuilder builder) throws IOException {
+  public static String runArtNoVerificationErrors(ArtCommandBuilder builder) throws IOException {
+    return runArtNoVerificationErrorsRaw(builder).stdout;
+  }
+
+  private static ProcessResult runArtProcessRaw(ArtCommandBuilder builder) throws IOException {
     Assume.assumeTrue(ToolHelper.artSupported());
     ProcessResult result;
     if (builder.isForDevice()) {
@@ -818,6 +840,11 @@
     } else {
       result = runProcess(builder.asProcessBuilder());
     }
+    return result;
+  }
+
+  private static ProcessResult runArtProcess(ArtCommandBuilder builder) throws IOException {
+    ProcessResult result = runArtProcessRaw(builder);
     if (result.exitCode != 0) {
       fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
     }
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 8cf6e9b..ab94090 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -39,6 +39,7 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
+import java.util.function.Consumer;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
 
@@ -80,6 +81,11 @@
     return ToolHelper.runD8(builder.build());
   }
 
+  protected AndroidApp compileWithD8(
+      JasminBuilder builder, Consumer<InternalOptions> optionsConsumer) throws Exception {
+    return ToolHelper.runD8(builder.build(), optionsConsumer);
+  }
+
   protected String runOnArtD8(JasminBuilder builder, String main) throws Exception {
     return runOnArt(compileWithD8(builder), main);
   }
@@ -132,10 +138,16 @@
     return ToolHelper.runArtNoVerificationErrors(dex.toString(), main);
   }
 
+  protected ProcessResult runOnArtRaw(AndroidApp app, String main) throws IOException {
+    Path out = temp.getRoot().toPath().resolve("out.zip");
+    app.writeToZip(out, OutputMode.Indexed);
+    return ToolHelper.runArtNoVerificationErrorsRaw(out.toString(), main);
+  }
+
   protected String runOnArt(AndroidApp app, String main) throws IOException {
     Path out = temp.getRoot().toPath().resolve("out.zip");
     app.writeToZip(out, OutputMode.Indexed);
-    return ToolHelper.runArtNoVerificationErrors(ImmutableList.of(out.toString()), main, null);
+    return ToolHelper.runArtNoVerificationErrors(out.toString(), main);
   }
 
   protected static DexApplication process(DexApplication app, InternalOptions options)
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java
new file mode 100644
index 0000000..6d9f582
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2017, 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.rewrite.staticvalues;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class B67468748 extends JasminTestBase {
+
+  @Test
+  public void jarInput() throws Exception {
+    final String CLASS_NAME = "Test";
+
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass(CLASS_NAME);
+
+    // The static field Test/intField is not defined even though it is written in the
+    // <clinit> code. This class cannot load, but we can still process it to dex, where Art also
+    // cannot load the class.
+    clazz.addStaticMethod("<clinit>", ImmutableList.of(), "V",
+        ".limit stack 1",
+        ".limit locals 1",
+        "iconst_1",
+        "putstatic Test/intField I",
+        "return");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "getstatic Test/intField I",
+        "invokevirtual java/io/PrintStream/print(I)V",
+        "return");
+
+    // The code does not run on the Java VM, as there is a missing field.
+    ProcessResult result = runOnJavaRaw(builder, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+
+    // Run in release mode to turn on initializer defaults rewriting.
+    AndroidApp application = compileWithD8(builder, options -> options.debug = false);
+
+    // The code does not run on Art, as there is a missing field.
+    result = runOnArtRaw(application, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+  }
+
+  // Test with dex input is in StaticValuesTest.
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index 9e5a839..1d26039 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -7,7 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.Sput;
 import com.android.tools.r8.code.SputObject;
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.InternalOptions;
@@ -572,4 +573,43 @@
     assertEquals(StringUtils.lines("2"), result);
   }
 
+  @Test
+  public void b67468748() throws Exception {
+    final String CLASS_NAME = "Test";
+
+    SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
+
+    // The static field LTest;->intField:I is not defined even though it is written in the
+    // <clinit> code. This class cannot load, but we can still process it to output which still
+    // cannot load.
+    builder.addStaticInitializer(
+        2,
+        "const               v0, 3",
+        "sput                v0, LTest;->intField:I",
+        "return-void"
+    );
+    builder.addMainMethod(
+        3,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget                v1, LTest;->intField:I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "return-void"
+    );
+
+    AndroidApp application = builder.build();
+
+    // The code does not run on Art, as there is a missing field.
+    ProcessResult result = runOnArtRaw(application, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+
+    // Run in release mode to turn on initializer defaults rewriting.
+    application = compileWithD8(application, options -> options.debug = false);
+
+    // The code does still not run on Art, as there is still a missing field.
+    result = runOnArtRaw(application, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index f2f1220..41f73d1 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -361,7 +361,7 @@
       getSource(currentClassName).add(builder.toString());
     }
 
-    public List<String> build() {
+    public List<String> buildSource() {
       List<String> result = new ArrayList<>(classes.size());
       for (String clazz : classes.keySet()) {
         Builder classBuilder = classes.get(clazz);
@@ -372,12 +372,18 @@
 
     public byte[] compile()
         throws IOException, RecognitionException, DexOverflowException, ExecutionException {
-      return Smali.compile(build());
+      return Smali.compile(buildSource());
     }
 
+    public AndroidApp build()
+        throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+      return AndroidApp.fromDexProgramData(compile());
+    }
+
+
     @Override
     public String toString() {
-      return String.join("\n\n", build());
+      return String.join("\n\n", buildSource());
     }
   }