Avoid tree shaking of fields that are read or written

Bug: 112290098
Change-Id: I9c238b70dae4d13f2b8b6d8299f29aab084a2fb1
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 962247e..b91c27d 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.KeyedDexItem;
@@ -21,6 +22,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
 
 public class TreePruner {
 
@@ -102,9 +104,9 @@
   }
 
   private <S extends PresortedComparable<S>, T extends KeyedDexItem<S>> int firstUnreachableIndex(
-      T[] items, Set<S> live) {
+      T[] items, Predicate<S> live) {
     for (int i = 0; i < items.length; i++) {
-      if (!live.contains(items[i].getKey())) {
+      if (!live.test(items[i].getKey())) {
         return i;
       }
     }
@@ -117,7 +119,7 @@
   }
 
   private DexEncodedMethod[] reachableMethods(DexEncodedMethod[] methods, DexClass clazz) {
-    int firstUnreachable = firstUnreachableIndex(methods, appInfo.liveMethods);
+    int firstUnreachable = firstUnreachableIndex(methods, appInfo.liveMethods::contains);
     // Return the original array if all methods are used.
     if (firstUnreachable == -1) {
       return methods;
@@ -168,7 +170,12 @@
   }
 
   private DexEncodedField[] reachableFields(DexEncodedField[] fields) {
-    int firstUnreachable = firstUnreachableIndex(fields, appInfo.liveFields);
+    Predicate<DexField> isReachableOrReferencedField =
+        field ->
+            appInfo.liveFields.contains(field)
+                || appInfo.fieldsRead.contains(field)
+                || appInfo.fieldsWritten.contains(field);
+    int firstUnreachable = firstUnreachableIndex(fields, isReachableOrReferencedField);
     // Return the original array if all fields are used.
     if (firstUnreachable == -1) {
       return fields;
@@ -177,14 +184,14 @@
       Log.debug(getClass(), "Removing field: " + fields[firstUnreachable]);
     }
     usagePrinter.printUnusedField(fields[firstUnreachable]);
-    ArrayList<DexEncodedField> reachableFields = new ArrayList<>(fields.length);
+    ArrayList<DexEncodedField> reachableOrReferencedFields = new ArrayList<>(fields.length);
     for (int i = 0; i < firstUnreachable; i++) {
-      reachableFields.add(fields[i]);
+      reachableOrReferencedFields.add(fields[i]);
     }
     for (int i = firstUnreachable + 1; i < fields.length; i++) {
       DexEncodedField field = fields[i];
-      if (appInfo.liveFields.contains(field.getKey())) {
-        reachableFields.add(field);
+      if (isReachableOrReferencedField.test(field.field)) {
+        reachableOrReferencedFields.add(field);
       } else {
         if (Log.ENABLED) {
           Log.debug(getClass(), "Removing field: " + field);
@@ -192,7 +199,9 @@
         usagePrinter.printUnusedField(field);
       }
     }
-    return reachableFields.toArray(new DexEncodedField[reachableFields.size()]);
+    return reachableOrReferencedFields.isEmpty()
+        ? DexEncodedField.EMPTY_ARRAY
+        : reachableOrReferencedFields.toArray(DexEncodedField.EMPTY_ARRAY);
   }
 
   public Collection<DexType> getRemovedClasses() {
diff --git a/src/test/java/com/android/tools/r8/shaking/B112290098.java b/src/test/java/com/android/tools/r8/shaking/B112290098.java
index 429853c..502da76 100644
--- a/src/test/java/com/android/tools/r8/shaking/B112290098.java
+++ b/src/test/java/com/android/tools/r8/shaking/B112290098.java
@@ -13,7 +13,6 @@
 
 public class B112290098 extends TestBase {
 
-  @Ignore("b/112290098")
   @Test
   public void test() throws Exception {
     String mainClass = TestClass.class.getName();