Add test for field removal

Bug: 33230777
Change-Id: I0aa76df03bc625a5e148b1af5ff68757a25831de
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 4d707a8..925ba5b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -135,7 +135,7 @@
    */
   protected AndroidApp compileWithR8(List<Class> classes, String proguardConfig)
       throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
-    return compileWithR8(readClasses(classes), writeTextToTempFile(proguardConfig));
+    return compileWithR8(readClasses(classes), proguardConfig);
   }
 
   /**
@@ -170,10 +170,22 @@
   /**
    * Compile an application with R8 using the supplied proguard configuration.
    */
+  protected AndroidApp compileWithR8(AndroidApp app, String proguardConfig)
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+    return compileWithR8(app, proguardConfig, null);
+  }
+
+  /**
+   * Compile an application with R8 using the supplied proguard configuration.
+   */
   protected AndroidApp compileWithR8(
       AndroidApp app, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
       throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
-    return compileWithR8(app, writeTextToTempFile(proguardConfig), optionsConsumer);
+    R8Command command =
+        ToolHelper.prepareR8CommandBuilder(app)
+            .addProguardConfiguration(ImmutableList.of(proguardConfig))
+            .build();
+    return ToolHelper.runR8(command, optionsConsumer);
   }
 
   /**
@@ -194,7 +206,15 @@
    * of the specified class.
    */
   public String keepMainProguardConfiguration(Class clazz) {
-    return "-keep public class " + clazz.getCanonicalName() + " {\n"
+    return keepMainProguardConfiguration(clazz.getCanonicalName());
+  }
+
+  /**
+   * Generate a Proguard configuration for keeping the "public static void main(String[])" method
+   * of the specified class.
+   */
+  public String keepMainProguardConfiguration(String clazz) {
+    return "-keep public class " + clazz + " {\n"
         + "  public static void main(java.lang.String[]);\n"
         + "}\n";
   }
diff --git a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
new file mode 100644
index 0000000..674124a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
@@ -0,0 +1,118 @@
+// 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.smali;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class RemoveWriteOfUnusedFieldsTest extends SmaliTestBase {
+
+  @Test
+  public void unreadStaticFieldsRemoved() throws Exception {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    // All these static fields are set but never read.
+    builder.addStaticField("booleanField", "Z");
+    builder.addStaticField("byteField", "B");
+    builder.addStaticField("shortField", "S");
+    builder.addStaticField("intField", "I");
+    builder.addStaticField("longField", "J");
+    builder.addStaticField("floatField", "F");
+    builder.addStaticField("doubleField", "D");
+    builder.addStaticField("charField", "C");
+    builder.addStaticField("objectField", "Ljava/lang/Object;");
+    builder.addStaticField("stringField", "Ljava/lang/String;");
+    builder.addStaticField("testField", "LTest;");
+
+    builder.addStaticMethod("void", "test", ImmutableList.of(),
+        2,
+        "const               v0, 0",
+        "sput-byte           v0, LTest;->booleanField:Z",
+        "sput-byte           v0, LTest;->byteField:B",
+        "sput-short          v0, LTest;->shortField:S",
+        "sput                v0, LTest;->intField:I",
+        "sput                v0, LTest;->floatField:F",
+        "sput-char           v0, LTest;->charField:C",
+        "sput-object         v0, LTest;->objectField:Ljava/lang/Object;",
+        "sput-object         v0, LTest;->stringField:Ljava/lang/String;",
+        "sput-object         v0, LTest;->testField:LTest;",
+        "const-wide          v0, 0",
+        "sput-wide           v0, LTest;->longField:J",
+        "sput-wide           v0, LTest;->doubleField:D",
+        "return-void");
+
+    builder.addMainMethod(
+        0,
+        "    invoke-static       { }, LTest;->test()V",
+        "    return-void                             ");
+
+    AndroidApp app = compileWithR8(
+        AndroidApp.fromDexProgramData(builder.compile()),
+        keepMainProguardConfiguration("Test"),
+        options -> options.inlineAccessors = false);
+
+    DexInspector inspector = new DexInspector(app);
+    MethodSubject method = inspector.clazz("Test").method("void", "test", ImmutableList.of());
+    DexCode code = method.getMethod().getCode().asDexCode();
+    assertTrue(code.isEmptyVoidMethod());
+  }
+
+  @Test
+  public void unreadInstanceFieldsRemoved() throws Exception {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    // All these instance fields are set but never read.
+    builder.addInstanceField("booleanField", "Z");
+    builder.addInstanceField("byteField", "B");
+    builder.addInstanceField("shortField", "S");
+    builder.addInstanceField("intField", "I");
+    builder.addInstanceField("longField", "J");
+    builder.addInstanceField("floatField", "F");
+    builder.addInstanceField("doubleField", "D");
+    builder.addInstanceField("charField", "C");
+    builder.addInstanceField("objectField", "Ljava/lang/Object;");
+    builder.addInstanceField("stringField", "Ljava/lang/String;");
+    builder.addInstanceField("testField", "LTest;");
+
+    builder.addInstanceMethod("void", "test", ImmutableList.of(),
+        2,
+        "const               v0, 0",
+        "iput-byte           v0, p0, LTest;->booleanField:Z",
+        "iput-byte           v0, p0, LTest;->byteField:B",
+        "iput-short          v0, p0, LTest;->shortField:S",
+        "iput                v0, p0, LTest;->intField:I",
+        "iput                v0, p0, LTest;->floatField:F",
+        "iput-char           v0, p0, LTest;->charField:C",
+        "iput-object         v0, p0, LTest;->objectField:Ljava/lang/Object;",
+        "iput-object         v0, p0, LTest;->stringField:Ljava/lang/String;",
+        "iput-object         v0, p0, LTest;->testField:LTest;",
+        "const-wide          v0, 0",
+        "iput-wide           v0, p0, LTest;->longField:J",
+        "iput-wide           v0, p0, LTest;->doubleField:D",
+        "return-void");
+
+    builder.addMainMethod(
+        1,
+        "    new-instance         v0, LTest;",
+        "    invoke-virtual       { v0 }, LTest;->test()V",
+        "    return-void                             ");
+
+    AndroidApp app = compileWithR8(
+        AndroidApp.fromDexProgramData(builder.compile()),
+        keepMainProguardConfiguration("Test"),
+        options -> options.inlineAccessors = false);
+
+    DexInspector inspector = new DexInspector(app);
+    MethodSubject method = inspector.clazz("Test").method("void", "test", ImmutableList.of());
+    DexCode code = method.getMethod().getCode().asDexCode();
+    assertTrue(code.isEmptyVoidMethod());
+  }
+}
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 e2006d0..56059f6 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -24,7 +24,6 @@
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
@@ -235,6 +234,45 @@
       addStaticField(name, type, null);
     }
 
+    public void addInstanceField(String name, String type) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(".field ");
+      builder.append(name);
+      builder.append(":");
+      builder.append(type);
+      getSource(currentClassName).add(builder.toString());
+    }
+
+    private MethodSignature addMethod(String flags, String returnType, String name,
+        List<String> parameters, int locals, String code) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(".method ");
+      if (flags != null && flags.length() > 0) {
+        builder.append(flags);
+        builder.append(" ");
+      }
+      builder.append(name);
+      builder.append("(");
+      for (String parameter : parameters) {
+        builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+      }
+      builder.append(")");
+      builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
+      builder.append("\n");
+      if (locals >= 0) {
+        builder.append(".locals ");
+        builder.append(locals);
+        builder.append("\n\n");
+        assert code != null;
+        builder.append(code);
+      } else {
+        assert code == null;
+      }
+      builder.append(".end method");
+      getSource(currentClassName).add(builder.toString());
+      return new MethodSignature(currentClassName, name, returnType, parameters);
+    }
+
     public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
         int locals, String... instructions) {
       StringBuilder builder = new StringBuilder();
@@ -266,43 +304,27 @@
     private MethodSignature addStaticMethod(String flags, String returnType, String name,
         List<String> parameters, int locals, String code) {
       StringBuilder builder = new StringBuilder();
-      builder.append(".method public static ");
-      if (flags != null && flags.length() > 0) {
-        builder.append(flags);
-        builder.append(" ");
-      }
-      builder.append(name);
-      builder.append("(");
-      for (String parameter : parameters) {
-        builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
-      }
-      builder.append(")");
-      builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
-      builder.append("\n");
-      builder.append(".locals ");
-      builder.append(locals);
-      builder.append("\n\n");
-      builder.append(code);
-      builder.append(".end method");
-      getSource(currentClassName).add(builder.toString());
-      return new MethodSignature(currentClassName, name, returnType, parameters);
+      return addMethod("public static " + flags, returnType, name, parameters, locals, code);
     }
 
     public MethodSignature addAbstractMethod(
         String returnType, String name, List<String> parameters) {
+      return addMethod("public abstract", returnType, name, parameters, -1, null);
+    }
+
+    public MethodSignature addInstanceMethod(String returnType, String name, List<String> parameters,
+        int locals, String... instructions) {
       StringBuilder builder = new StringBuilder();
-      builder.append(".method public abstract ");
-      builder.append(name);
-      builder.append("(");
-      for (String parameter : parameters) {
-        builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+      for (String instruction : instructions) {
+        builder.append(instruction);
+        builder.append("\n");
       }
-      builder.append(")");
-      builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
-      builder.append("\n");
-      builder.append(".end method");
-      getSource(currentClassName).add(builder.toString());
-      return new MethodSignature(currentClassName, name, returnType, parameters);
+      return addInstanceMethod(returnType, name, parameters, locals, builder.toString());
+    }
+
+    public MethodSignature addInstanceMethod(String returnType, String name, List<String> parameters,
+        int locals, String code) {
+      return addMethod("public", returnType, name, parameters, locals, code);
     }
 
     public MethodSignature addMainMethod(int locals, String... instructions) {