Revert "Remove all uses of definitionFor(DexField)"

This reverts commit e987cb1c67ce17c09e7f4725dc308f92ff27d2d5.

Revert "Remove field definitions cache"

This reverts commit 76dff502470f97379464d91c5da3703d60b0f229.

Change-Id: Iff21cf096d05e50230f28a88a3d3629d05f34b25
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 3706dec..e49f177 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -21,6 +21,9 @@
   private final DexApplication app;
   private final DexItemFactory dexItemFactory;
 
+  // TODO(b/151804585): Remove this cache.
+  private final ConcurrentHashMap<DexType, Map<DexField, DexEncodedField>> fieldDefinitionsCache;
+
   // For some optimizations, e.g. optimizing synthetic classes, we may need to resolve the current
   // class being optimized.
   private final ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses;
@@ -30,28 +33,31 @@
   private final BooleanBox obsolete;
 
   public AppInfo(DexApplication application) {
-    this(application, new ConcurrentHashMap<>(), new BooleanBox());
+    this(application, new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new BooleanBox());
   }
 
   // For desugaring.
   protected AppInfo(AppInfo appInfo) {
-    this(appInfo.app, appInfo.synthesizedClasses, appInfo.obsolete);
+    this(appInfo.app, appInfo.fieldDefinitionsCache, appInfo.synthesizedClasses, appInfo.obsolete);
   }
 
   // For AppInfoWithLiveness.
   protected AppInfo(AppInfoWithClassHierarchy previous) {
     this(
         ((AppInfo) previous).app,
+        new ConcurrentHashMap<>(((AppInfo) previous).fieldDefinitionsCache),
         new ConcurrentHashMap<>(((AppInfo) previous).synthesizedClasses),
         new BooleanBox());
   }
 
   private AppInfo(
       DexApplication application,
+      ConcurrentHashMap<DexType, Map<DexField, DexEncodedField>> fieldDefinitionsCache,
       ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses,
       BooleanBox obsolete) {
     this.app = application;
     this.dexItemFactory = application.dexItemFactory;
+    this.fieldDefinitionsCache = fieldDefinitionsCache;
     this.synthesizedClasses = synthesizedClasses;
     this.obsolete = obsolete;
   }
@@ -96,6 +102,7 @@
     assert checkIfObsolete();
     assert clazz.type.isD8R8SynthesizedClassType();
     DexProgramClass previous = synthesizedClasses.put(clazz.type, clazz);
+    invalidateFieldCacheFor(clazz.type);
     assert previous == null || previous == clazz;
   }
 
@@ -176,6 +183,21 @@
     return clazz.getMethodCollection().getMethod(method);
   }
 
+  @Deprecated
+  @Override
+  public DexEncodedField definitionFor(DexField field) {
+    assert checkIfObsolete();
+    return getFieldDefinitions(field.holder).get(field);
+  }
+
+  private Map<DexField, DexEncodedField> getFieldDefinitions(DexType type) {
+    return fieldDefinitionsCache.computeIfAbsent(type, this::computeFieldDefinitions);
+  }
+
+  public void invalidateFieldCacheFor(DexType type) {
+    fieldDefinitionsCache.remove(type);
+  }
+
   /**
    * Lookup static method on the method holder, or answers null.
    *
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index b4534f5..33c63dc 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -228,6 +228,12 @@
 
   @Deprecated
   @Override
+  public final DexEncodedField definitionFor(DexField field) {
+    return appInfo().definitionFor(field);
+  }
+
+  @Deprecated
+  @Override
   public final DexEncodedMethod definitionFor(DexMethod method) {
     return appInfo().definitionFor(method);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
index 214e41c..5055cc5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -7,6 +7,9 @@
 public interface DexDefinitionSupplier {
 
   @Deprecated
+  DexEncodedField definitionFor(DexField field);
+
+  @Deprecated
   DexEncodedMethod definitionFor(DexMethod method);
 
   DexClass definitionFor(DexType type);
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 84d581e..31017af 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -7,8 +7,6 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.naming.NamingLens;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 public class DexField extends DexMember<DexEncodedField, DexField> {
 
@@ -26,28 +24,7 @@
   }
 
   @Override
-  public DexEncodedField lookupOnClass(DexClass clazz) {
-    return clazz != null ? clazz.lookupField(this) : null;
-  }
-
-  @Override
-  public <T> T apply(
-      Function<DexType, T> classConsumer,
-      Function<DexField, T> fieldConsumer,
-      Function<DexMethod, T> methodConsumer) {
-    return fieldConsumer.apply(this);
-  }
-
-  @Override
-  public void accept(
-      Consumer<DexType> classConsumer,
-      Consumer<DexField> fieldConsumer,
-      Consumer<DexMethod> methodConsumer) {
-    fieldConsumer.accept(this);
-  }
-
-  @Override
-  public <T> void accept(
+  public <T> void apply(
       BiConsumer<DexType, T> classConsumer,
       BiConsumer<DexField, T> fieldConsumer,
       BiConsumer<DexMethod, T> methodConsumer,
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 02fd431..d04808b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -13,10 +13,6 @@
     this.holder = holder;
   }
 
-  public DexEncodedMember<?, ?> lookupOnClass(DexClass clazz) {
-    return clazz != null ? clazz.lookupMember(this) : null;
-  }
-
   public abstract boolean match(R entry);
 
   public abstract boolean match(D entry);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 3618578..89c8f05 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -12,8 +12,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 public class DexMethod extends DexMember<DexEncodedMethod, DexMethod> {
 
@@ -32,23 +30,7 @@
   }
 
   @Override
-  public <T> T apply(
-      Function<DexType, T> classConsumer,
-      Function<DexField, T> fieldConsumer,
-      Function<DexMethod, T> methodConsumer) {
-    return methodConsumer.apply(this);
-  }
-
-  @Override
-  public void accept(
-      Consumer<DexType> classConsumer,
-      Consumer<DexField> fieldConsumer,
-      Consumer<DexMethod> methodConsumer) {
-    methodConsumer.accept(this);
-  }
-
-  @Override
-  public <T> void accept(
+  public <T> void apply(
       BiConsumer<DexType, T> classConsumer,
       BiConsumer<DexField, T> fieldConsumer,
       BiConsumer<DexMethod, T> methodConsumer,
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 0fe1d69..900712b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -133,10 +133,6 @@
     synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom);
   }
 
-  public void forEachProgramField(Consumer<ProgramField> consumer) {
-    forEachField(field -> consumer.accept(new ProgramField(this, field)));
-  }
-
   public void forEachProgramMethod(Consumer<ProgramMethod> consumer) {
     forEachProgramMethodMatching(alwaysTrue(), consumer);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexReference.java b/src/main/java/com/android/tools/r8/graph/DexReference.java
index c37c9de..3735378 100644
--- a/src/main/java/com/android/tools/r8/graph/DexReference.java
+++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.graph;
 
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
@@ -14,17 +13,7 @@
  */
 public abstract class DexReference extends IndexedDexItem {
 
-  public abstract <T> T apply(
-      Function<DexType, T> classConsumer,
-      Function<DexField, T> fieldConsumer,
-      Function<DexMethod, T> methodConsumer);
-
-  public abstract void accept(
-      Consumer<DexType> classConsumer,
-      Consumer<DexField> fieldConsumer,
-      Consumer<DexMethod> methodConsumer);
-
-  public abstract <T> void accept(
+  public abstract <T> void apply(
       BiConsumer<DexType, T> classConsumer,
       BiConsumer<DexField, T> fieldConsumer,
       BiConsumer<DexMethod, T> methodConsumer,
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index fc7ae3e..dfe07f6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -31,8 +31,6 @@
 import java.util.List;
 import java.util.Set;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Predicate;
 
 public class DexType extends DexReference implements PresortedComparable<DexType> {
@@ -118,23 +116,7 @@
   }
 
   @Override
-  public <T> T apply(
-      Function<DexType, T> classConsumer,
-      Function<DexField, T> fieldConsumer,
-      Function<DexMethod, T> methodConsumer) {
-    return classConsumer.apply(this);
-  }
-
-  @Override
-  public void accept(
-      Consumer<DexType> classConsumer,
-      Consumer<DexField> fieldConsumer,
-      Consumer<DexMethod> methodConsumer) {
-    classConsumer.accept(this);
-  }
-
-  @Override
-  public <T> void accept(
+  public <T> void apply(
       BiConsumer<DexType, T> classConsumer,
       BiConsumer<DexField, T> fieldConsumer,
       BiConsumer<DexMethod, T> methodConsumer,
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 3699d2e..d66ef7e 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -75,6 +75,13 @@
 
   @Deprecated
   @Override
+  public DexEncodedField definitionFor(DexField field) {
+    DexClass clazz = definitionFor(field.holder);
+    return clazz != null ? clazz.lookupField(field) : null;
+  }
+
+  @Deprecated
+  @Override
   public DexEncodedMethod definitionFor(DexMethod method) {
     DexClass clazz = definitionFor(method.holder);
     return clazz != null ? clazz.lookupMethod(method) : null;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 01d86a1..fbb42a9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -7,10 +7,8 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.graph.AppView;
-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.ProgramMethod;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -108,9 +106,8 @@
       }
       AbstractValue abstractValue = root.getAbstractValue(appView, context);
       if (abstractValue.isSingleFieldValue()) {
-        DexField fieldReference = abstractValue.asSingleFieldValue().getField();
-        DexClass holder = appView.definitionForHolder(fieldReference);
-        DexEncodedField field = fieldReference.lookupOnClass(holder);
+        DexEncodedField field =
+            appView.definitionFor(abstractValue.asSingleFieldValue().getField());
         if (field != null && field.isEnum()) {
           return false;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index 9fe885d..a2a932c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -5,9 +5,7 @@
 package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.SetUtils;
@@ -73,14 +71,12 @@
     assert !isEmpty();
     ConcreteMutableFieldSet rewrittenSet = new ConcreteMutableFieldSet();
     for (DexEncodedField field : fields) {
-      DexField rewrittenFieldReference = lens.lookupField(field.field);
-      DexClass holder = appView.definitionForHolder(rewrittenFieldReference);
-      DexEncodedField rewrittenField = rewrittenFieldReference.lookupOnClass(holder);
+      DexEncodedField rewrittenField = appView.definitionFor(lens.lookupField(field.field));
       if (rewrittenField == null) {
         assert false;
         continue;
       }
-      rewrittenSet.add(rewrittenField);
+      rewrittenSet.add(field);
     }
     return rewrittenSet;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
index ba49fc0..e66d342 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -85,20 +84,16 @@
             }
           }
         } else if (instruction.isFieldInstruction()) {
-          // Since we only need to desugar accesses to private fields, and all accesses to private
-          // fields must be accessing the private field directly on its holder, we can lookup the
-          // field on the holder instead of resolving the field.
-          FieldInstruction fieldInstruction = instruction.asFieldInstruction();
-          DexClass holder = appView.definitionForHolder(fieldInstruction.getField());
-          DexEncodedField field = fieldInstruction.getField().lookupOnClass(holder);
-          if (field != null && fieldAccessRequiresRewriting(field, method)) {
+          DexEncodedField encodedField =
+              appView.definitionFor(instruction.asFieldInstruction().getField());
+          if (encodedField != null && fieldAccessRequiresRewriting(encodedField, method)) {
             if (instruction.isInstanceGet() || instruction.isStaticGet()) {
-              DexMethod bridge = ensureFieldAccessBridge(field, true);
+              DexMethod bridge = ensureFieldAccessBridge(encodedField, true);
               instructions.replaceCurrentInstruction(
                   new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
             } else {
               assert instruction.isInstancePut() || instruction.isStaticPut();
-              DexMethod bridge = ensureFieldAccessBridge(field, false);
+              DexMethod bridge = ensureFieldAccessBridge(encodedField, false);
               instructions.replaceCurrentInstruction(
                   new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
             }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 2586141..7da225c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -84,15 +84,14 @@
     return appView.definitionFor(appView.graphLense().lookupType(type));
   }
 
-  private DexEncodedMethod lookupOnHolder(
+  private DexEncodedMethod definitionFor(
       DexMethod method, DexClassAndMethod context, Invoke.Type invokeType) {
     return appView.definitionFor(
         appView.graphLense().lookupMethod(method, context.getReference(), invokeType).getMethod());
   }
 
-  private DexEncodedField lookupOnHolder(DexField field) {
-    DexField rewritten = appView.graphLense().lookupField(field);
-    return rewritten.lookupOnClass(appView.definitionForHolder(rewritten));
+  private DexEncodedField definitionFor(DexField field) {
+    return appView.definitionFor(appView.graphLense().lookupField(field));
   }
 
   // Extract the list of types in the programClass' nest, of host hostClass
@@ -366,7 +365,7 @@
       if (!method.holder.isClassType()) {
         return false;
       }
-      DexEncodedMethod encodedMethod = lookupOnHolder(method, context, invokeType);
+      DexEncodedMethod encodedMethod = definitionFor(method, context, invokeType);
       if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, context)) {
         ensureInvokeBridge(encodedMethod);
         return true;
@@ -375,10 +374,7 @@
     }
 
     private boolean registerFieldAccess(DexField field, boolean isGet) {
-      // Since we only need to desugar accesses to private fields, and all accesses to private
-      // fields must be accessing the private field directly on its holder, we can lookup the field
-      // on the holder instead of resolving the field.
-      DexEncodedField encodedField = lookupOnHolder(field);
+      DexEncodedField encodedField = definitionFor(field);
       if (encodedField != null && fieldAccessRequiresRewriting(encodedField, context)) {
         ensureFieldAccessBridge(encodedField, isGet);
         return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index def219f..f2cefad 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2537,13 +2537,10 @@
                 if (singleFieldValue.getField() == otherSingleFieldValue.getField()) {
                   simplifyIfWithKnownCondition(code, block, theIf, 0);
                 } else {
-                  DexClass holder = appView.definitionForHolder(singleFieldValue.getField());
-                  DexEncodedField field = singleFieldValue.getField().lookupOnClass(holder);
+                  DexEncodedField field = appView.definitionFor(singleFieldValue.getField());
                   if (field != null && field.isEnum()) {
-                    DexClass otherHolder =
-                        appView.definitionForHolder(otherSingleFieldValue.getField());
                     DexEncodedField otherField =
-                        otherSingleFieldValue.getField().lookupOnClass(otherHolder);
+                        appView.definitionFor(otherSingleFieldValue.getField());
                     if (otherField != null && otherField.isEnum()) {
                       simplifyIfWithKnownCondition(code, block, theIf, 1);
                     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index 5178020..ef81097 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -155,7 +155,7 @@
       return appView.appInfo().withLiveness().resolveField(field).getResolvedField();
     }
     if (field.holder == method.getHolderType()) {
-      return method.getHolder().lookupField(field);
+      return appView.definitionFor(field);
     }
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
index fe70918..2fe8a19 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -4,9 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.info.field;
 
-
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
@@ -34,10 +32,9 @@
       BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
     infos.forEach(
         (field, info) -> {
-          DexClass holder = definitions.definitionForHolder(field);
-          DexEncodedField definition = field.lookupOnClass(holder);
-          if (definition != null) {
-            consumer.accept(definition, info);
+          DexEncodedField encodedField = definitions.definitionFor(field);
+          if (encodedField != null) {
+            consumer.accept(encodedField, info);
           } else {
             assert false;
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index bcea380..ad6313e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -65,7 +65,7 @@
     for (LibraryMembers libraryMembers : appView.dexItemFactory().libraryMembersCollection) {
       libraryMembers.forEachFinalField(
           field -> {
-            DexEncodedField definition = field.lookupOnClass(appView.definitionForHolder(field));
+            DexEncodedField definition = appView.definitionFor(field);
             if (definition != null) {
               if (definition.isFinal()) {
                 finalLibraryFields.add(definition);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index c326f61..f3fba2d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -12,7 +12,9 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
+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.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -120,6 +122,11 @@
     }
 
     @Override
+    public DexEncodedField definitionFor(DexField field) {
+      throw new Unreachable("Should not be called");
+    }
+
+    @Override
     public DexEncodedMethod definitionFor(DexMethod method) {
       throw new Unreachable("Should not be called");
     }
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index b014c4f..8cffa05 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -3,19 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
-import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -182,7 +178,9 @@
                   getOrCreateReservedFieldNamingState(clazz.type);
               FieldNamingState state = parentState.createChildState(reservedNames);
               if (clazz.isProgramClass()) {
-                clazz.asProgramClass().forEachProgramField(field -> renameField(field, state));
+                for (DexEncodedField field : clazz.fields()) {
+                  renameField(field, state);
+                }
               }
 
               assert !states.containsKey(clazz.type);
@@ -214,14 +212,11 @@
     for (DexClass clazz : partition) {
       if (clazz.isProgramClass()) {
         assert clazz.isInterface();
-        clazz
-            .asProgramClass()
-            .forEachProgramField(
-                field -> {
-                  DexString newName = renameField(field, state);
-                  namesToBeReservedInImplementsSubclasses.markReservedDirectly(
-                      newName, field.getReference().name, field.getReference().type);
-                });
+        for (DexEncodedField field : clazz.fields()) {
+          DexString newName = renameField(field, state);
+          namesToBeReservedInImplementsSubclasses.markReservedDirectly(
+              newName, field.field.name, field.field.type);
+        }
       }
     }
 
@@ -241,10 +236,11 @@
     }
   }
 
-  private DexString renameField(ProgramField field, FieldNamingState state) {
+  private DexString renameField(DexEncodedField encodedField, FieldNamingState state) {
+    DexField field = encodedField.field;
     DexString newName = state.getOrCreateNameFor(field);
-    if (newName != field.getReference().name) {
-      renaming.put(field.getReference(), newName);
+    if (newName != field.name) {
+      renaming.put(field, newName);
     }
     return newName;
   }
@@ -260,17 +256,32 @@
   }
 
   private void renameNonReboundAccessToField(DexField field) {
-    // If the given field reference is a non-rebound reference to a program field, then assign the
-    // same name as the resolved field.
+    // Already renamed
     if (renaming.containsKey(field)) {
       return;
     }
-    DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(field));
-    if (holder == null) {
+    DexEncodedField definition = appView.definitionFor(field);
+    if (definition != null) {
+      assert definition.field == field;
       return;
     }
-    DexEncodedField definition = appView.appInfo().resolveFieldOn(holder, field).getResolvedField();
-    if (definition != null && definition.field != field && renaming.containsKey(definition.field)) {
+    // Now, `field` is reference. Find its definition and check if it's renamed.
+    DexClass holder = appView.definitionFor(field.holder);
+    // We don't care pruned types or library classes.
+    if (holder == null || holder.isNotProgramClass()) {
+      return;
+    }
+    definition = appView.appInfo().resolveField(field).getResolvedField();
+    if (definition == null) {
+      // The program is already broken in the sense that it has an unresolvable field reference.
+      // Leave it as-is.
+      return;
+    }
+    assert definition.field != field;
+    assert definition.holder() != field.holder;
+    // If the definition is renamed,
+    if (renaming.containsKey(definition.field)) {
+      // Assign the same, renamed name as the definition to the reference.
       renaming.put(field, renaming.get(definition.field));
     }
   }
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
index 35e3d3f..f8d80cb 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -6,9 +6,11 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.naming.FieldNamingState.InternalState;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -18,7 +20,7 @@
 
   private final ReservedFieldNamingState reservedNames;
   private final MemberNamingStrategy strategy;
-  private final BiPredicate<DexString, ProgramField> isAvailable;
+  private final BiPredicate<DexString, DexField> isAvailable;
 
   public FieldNamingState(
       AppView<? extends AppInfoWithClassHierarchy> appView, MemberNamingStrategy strategy) {
@@ -40,8 +42,7 @@
     super(appView, internalStates);
     this.reservedNames = reservedNames;
     this.strategy = strategy;
-    this.isAvailable =
-        (newName, field) -> !reservedNames.isReserved(newName, field.getReference().type);
+    this.isAvailable = (newName, field) -> !reservedNames.isReserved(newName, field.type);
   }
 
   public FieldNamingState createChildState(ReservedFieldNamingState reservedNames) {
@@ -51,13 +52,20 @@
     return childState;
   }
 
-  public DexString getOrCreateNameFor(ProgramField field) {
-    DexString reservedName = strategy.getReservedName(field.getDefinition(), field.getHolder());
-    if (reservedName != null) {
-      return reservedName;
+  public DexString getOrCreateNameFor(DexField field) {
+    DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
+    if (encodedField != null) {
+      DexClass clazz = appView.definitionFor(encodedField.holder());
+      if (clazz == null) {
+        return field.name;
+      }
+      DexString reservedName = strategy.getReservedName(encodedField, clazz);
+      if (reservedName != null) {
+        return reservedName;
+      }
     }
     // TODO(b/133208730) If we cannot resolve the field, are we then allowed to rename it?
-    return getOrCreateInternalState(field.getReference()).createNewName(field);
+    return getOrCreateInternalState(field).createNewName(field);
   }
 
   public void includeReservations(ReservedFieldNamingState reservedNames) {
@@ -92,9 +100,9 @@
       this.nextNameIndex = nextNameIndex;
     }
 
-    public DexString createNewName(ProgramField field) {
+    public DexString createNewName(DexField field) {
       DexString name = strategy.next(field, this, isAvailable);
-      assert !reservedNames.isReserved(name, field.getReference().type);
+      assert !reservedNames.isReserved(name, field.type);
       return name;
     }
 
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java b/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java
index cddbea8..bacc7e0 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java
@@ -7,9 +7,9 @@
 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.DexMethod;
 import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.ProgramField;
 import java.util.function.BiPredicate;
 
 public interface MemberNamingStrategy {
@@ -20,9 +20,9 @@
       BiPredicate<DexString, DexMethod> isAvailable);
 
   DexString next(
-      ProgramField field,
+      DexField field,
       InternalNamingState internalState,
-      BiPredicate<DexString, ProgramField> isAvailable);
+      BiPredicate<DexString, DexField> isAvailable);
 
   DexString getReservedName(DexEncodedMethod method, DexClass holder);
 
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index d0f0381..8f1a41a 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -11,11 +11,11 @@
 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.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.kotlin.KotlinMetadataRewriter;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassNamingStrategy;
@@ -240,10 +240,10 @@
 
     @Override
     public DexString next(
-        ProgramField field,
+        DexField field,
         InternalNamingState internalState,
-        BiPredicate<DexString, ProgramField> isAvailable) {
-      assert checkAllowMemberRenaming(field.getHolderType());
+        BiPredicate<DexString, DexField> isAvailable) {
+      assert checkAllowMemberRenaming(field.holder);
       DexString candidate;
       do {
         candidate = getNextName(internalState, false);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index e720b30..3ce8e7f 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.kotlin.KotlinMetadataRewriter;
@@ -33,6 +32,7 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.TriFunction;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.Maps;
@@ -301,9 +301,8 @@
       DexField originalField = ((FieldSignature) signature).toDexField(factory, type);
       addMemberNaming(
           originalField, memberNaming, addToAdditionalMaps ? additionalFieldNamings : null);
-      DexClass holder = appView.definitionForHolder(originalField);
-      DexEncodedField field = originalField.lookupOnClass(holder);
-      if (field == null || !field.isPrivate()) {
+      DexEncodedField encodedField = appView.definitionFor(originalField);
+      if (encodedField == null || !encodedField.accessFlags.isPrivate()) {
         nonPrivateMembers.put(originalField, memberNaming);
       }
     }
@@ -466,46 +465,62 @@
 
     @Override
     public DexString next(
-        DexMethod reference,
+        DexMethod method,
         InternalNamingState internalState,
         BiPredicate<DexString, DexMethod> isAvailable) {
-      DexClass holder = appView.definitionForHolder(reference);
-      assert holder != null;
-      DexEncodedMethod method = holder.lookupMethod(reference);
-      DexString reservedName = getReservedName(method, reference.name, holder);
-      DexString nextName;
-      if (reservedName != null) {
-        if (!isAvailable.test(reservedName, reference)) {
-          reportReservationError(reference, reservedName);
-        }
-        nextName = reservedName;
-      } else {
-        assert !mappedNames.containsKey(reference);
-        assert appView.rootSet().mayBeMinified(reference, appView);
-        nextName = super.next(reference, internalState, isAvailable);
-      }
-      assert nextName == reference.name || !method.isInitializer();
-      assert nextName == reference.name || !holder.isAnnotation();
+      DexEncodedMethod definition = appView.definitionFor(method);
+      DexString nextName =
+          nextName(
+              method,
+              definition,
+              method.name,
+              method.holder,
+              internalState,
+              isAvailable,
+              super::next);
+      assert nextName == method.name || !definition.isClassInitializer();
+      assert nextName == method.name
+          || !appView.definitionFor(method.holder).accessFlags.isAnnotation();
       return nextName;
     }
 
     @Override
     public DexString next(
-        ProgramField field,
+        DexField field,
         InternalNamingState internalState,
-        BiPredicate<DexString, ProgramField> isAvailable) {
-      DexField reference = field.getReference();
-      DexString reservedName =
-          getReservedName(field.getDefinition(), reference.name, field.getHolder());
+        BiPredicate<DexString, DexField> isAvailable) {
+      return nextName(
+          field,
+          appView.definitionFor(field),
+          field.name,
+          field.holder,
+          internalState,
+          isAvailable,
+          super::next);
+    }
+
+    private <T extends DexReference> DexString nextName(
+        T reference,
+        DexDefinition definition,
+        DexString name,
+        DexType holderType,
+        InternalNamingState internalState,
+        BiPredicate<DexString, T> isAvailable,
+        TriFunction<T, InternalNamingState, BiPredicate<DexString, T>, DexString> generateName) {
+      assert definition.isDexEncodedMethod() || definition.isDexEncodedField();
+      assert definition.toReference() == reference;
+      DexClass holder = appView.definitionFor(holderType);
+      assert holder != null;
+      DexString reservedName = getReservedName(definition, name, holder);
       if (reservedName != null) {
-        if (!isAvailable.test(reservedName, field)) {
-          reportReservationError(reference, reservedName);
+        if (!isAvailable.test(reservedName, reference)) {
+          reportReservationError(definition.toReference(), reservedName);
         }
         return reservedName;
       }
       assert !mappedNames.containsKey(reference);
       assert appView.rootSet().mayBeMinified(reference, appView);
-      return super.next(field, internalState, isAvailable);
+      return generateName.apply(reference, internalState, isAvailable);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 9dfa92e..df5a09c 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -26,7 +26,6 @@
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
-import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
 import com.android.tools.r8.graph.InstantiatedSubTypeInfo;
@@ -976,9 +975,8 @@
     // Switchmap classes should never be affected by renaming.
     assert lens.assertDefinitionsNotModified(
         switchMaps.keySet().stream()
-            .map(this::resolveField)
-            .filter(FieldResolutionResult::isSuccessfulResolution)
-            .map(FieldResolutionResult::getResolvedField)
+            .map(this::definitionFor)
+            .filter(Objects::nonNull)
             .collect(Collectors.toList()));
 
     assert lens.assertDefinitionsNotModified(
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index ff991cf..8329ac7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -504,6 +504,14 @@
     recordTypeReference(field.type);
   }
 
+  public DexEncodedField definitionFor(DexField field) {
+    DexClass clazz = definitionFor(field.holder);
+    if (clazz == null) {
+      return null;
+    }
+    return clazz.lookupField(field);
+  }
+
   public DexEncodedMethod definitionFor(DexMethod method) {
     DexClass clazz = definitionFor(method.holder);
     if (clazz == null) {
@@ -3341,7 +3349,7 @@
     ProgramMethod methodToKeep = action.getMethodToKeep();
     ProgramMethod singleTarget = action.getSingleTarget();
     DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
-    if (rootSet.noShrinking.containsMethod(singleTarget.getReference())) {
+    if (rootSet.noShrinking.containsKey(singleTargetMethod.method)) {
       return;
     }
     if (methodToKeep != singleTarget) {
@@ -4163,6 +4171,12 @@
 
     @Deprecated
     @Override
+    public DexEncodedField definitionFor(DexField field) {
+      return enqueuer.definitionFor(field);
+    }
+
+    @Deprecated
+    @Override
     public DexEncodedMethod definitionFor(DexMethod method) {
       return enqueuer.definitionFor(method);
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index af14927..3bbfa9b 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -55,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -81,7 +82,7 @@
   private final SubtypingInfo subtypingInfo;
   private final DirectMappedDexApplication application;
   private final Iterable<? extends ProguardConfigurationRule> rules;
-  private final MutableItemsWithRules noShrinking = new MutableItemsWithRules();
+  private final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking = new IdentityHashMap<>();
   private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
   private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
   private final LinkedHashMap<DexReference, DexReference> checkDiscarded = new LinkedHashMap<>();
@@ -1099,7 +1100,7 @@
               .computeIfAbsent(precondition.toReference(), x -> new MutableItemsWithRules())
               .addReferenceWithRule(item.toReference(), keepRule);
         } else {
-          noShrinking.addReferenceWithRule(item.toReference(), keepRule);
+          noShrinking.computeIfAbsent(item.toReference(), i -> new HashSet<>()).add(keepRule);
         }
         context.markAsUsed();
       }
@@ -1267,7 +1268,7 @@
 
     final Set<DexMethod> neverInline;
     final Set<DexType> neverClassInline;
-    final MutableItemsWithRules noShrinking;
+    final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking;
     final Set<DexReference> noObfuscation;
     final Map<DexReference, MutableItemsWithRules> dependentNoShrinking;
     final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
@@ -1276,7 +1277,7 @@
     RootSetBase(
         Set<DexMethod> neverInline,
         Set<DexType> neverClassInline,
-        MutableItemsWithRules noShrinking,
+        Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
         Set<DexReference> noObfuscation,
         Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
@@ -1402,45 +1403,16 @@
       return MutableItemsWithRules.EMPTY;
     }
 
-    public abstract boolean containsClass(DexType type);
-
-    public abstract boolean containsField(DexField field);
-
-    public abstract boolean containsMethod(DexMethod method);
-
-    public final boolean containsReference(DexReference reference) {
-      return reference.apply(this::containsClass, this::containsField, this::containsMethod);
-    }
-
-    public abstract void forEachClass(Consumer<DexType> consumer);
-
     public abstract void forEachClass(BiConsumer<DexType, Set<ProguardKeepRuleBase>> consumer);
 
-    public abstract void forEachField(Consumer<? super DexField> consumer);
-
     public abstract void forEachField(
         BiConsumer<? super DexField, Set<ProguardKeepRuleBase>> consumer);
 
-    public abstract void forEachMember(Consumer<DexMember<?, ?>> consumer);
-
     public abstract void forEachMember(
         BiConsumer<DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer);
 
-    public abstract void forEachMethod(Consumer<? super DexMethod> consumer);
-
     public abstract void forEachMethod(
         BiConsumer<? super DexMethod, Set<ProguardKeepRuleBase>> consumer);
-
-    public abstract Set<ProguardKeepRuleBase> getRulesForClass(DexType type);
-
-    public abstract Set<ProguardKeepRuleBase> getRulesForField(DexField field);
-
-    public abstract Set<ProguardKeepRuleBase> getRulesForMethod(DexMethod method);
-
-    public final Set<ProguardKeepRuleBase> getRulesForReference(DexReference reference) {
-      return reference.apply(
-          this::getRulesForClass, this::getRulesForField, this::getRulesForMethod);
-    }
   }
 
   static class MutableItemsWithRules extends ItemsWithRules {
@@ -1467,136 +1439,48 @@
     }
 
     public void addAll(ItemsWithRules items) {
-      items.forEachClass(this::addClassWithRules);
-      items.forEachField(this::addFieldWithRules);
-      items.forEachMethod(this::addMethodWithRules);
+      items.forEachClass(classesWithRules::put);
+      items.forEachField(fieldsWithRules::put);
+      items.forEachMethod(methodsWithRules::put);
     }
 
     public void addClassWithRule(DexType type, ProguardKeepRuleBase rule) {
       classesWithRules.computeIfAbsent(type, ignore -> new HashSet<>()).add(rule);
     }
 
-    public void addClassWithRules(DexType type, Set<ProguardKeepRuleBase> rules) {
-      classesWithRules.computeIfAbsent(type, ignore -> new HashSet<>()).addAll(rules);
-    }
-
     public void addFieldWithRule(DexField field, ProguardKeepRuleBase rule) {
       fieldsWithRules.computeIfAbsent(field, ignore -> new HashSet<>()).add(rule);
     }
 
-    public void addFieldWithRules(DexField field, Set<ProguardKeepRuleBase> rules) {
-      fieldsWithRules.computeIfAbsent(field, ignore -> new HashSet<>()).addAll(rules);
-    }
-
     public void addMethodWithRule(DexMethod method, ProguardKeepRuleBase rule) {
       methodsWithRules.computeIfAbsent(method, ignore -> new HashSet<>()).add(rule);
     }
 
-    public void addMethodWithRules(DexMethod method, Set<ProguardKeepRuleBase> rules) {
-      methodsWithRules.computeIfAbsent(method, ignore -> new HashSet<>()).addAll(rules);
-    }
-
     public void addReferenceWithRule(DexReference reference, ProguardKeepRuleBase rule) {
-      reference.accept(
+      reference.apply(
           this::addClassWithRule, this::addFieldWithRule, this::addMethodWithRule, rule);
     }
 
-    public void addReferenceWithRules(DexReference reference, Set<ProguardKeepRuleBase> rules) {
-      reference.accept(
-          this::addClassWithRules, this::addFieldWithRules, this::addMethodWithRules, rules);
-    }
-
-    @Override
-    public boolean containsClass(DexType type) {
-      return classesWithRules.containsKey(type);
-    }
-
-    @Override
-    public boolean containsField(DexField field) {
-      return fieldsWithRules.containsKey(field);
-    }
-
-    @Override
-    public boolean containsMethod(DexMethod method) {
-      return methodsWithRules.containsKey(method);
-    }
-
-    @Override
-    public void forEachClass(Consumer<DexType> consumer) {
-      classesWithRules.keySet().forEach(consumer);
-    }
-
     @Override
     public void forEachClass(BiConsumer<DexType, Set<ProguardKeepRuleBase>> consumer) {
       classesWithRules.forEach(consumer);
     }
 
     @Override
-    public void forEachField(Consumer<? super DexField> consumer) {
-      fieldsWithRules.keySet().forEach(consumer);
-    }
-
-    @Override
     public void forEachField(BiConsumer<? super DexField, Set<ProguardKeepRuleBase>> consumer) {
       fieldsWithRules.forEach(consumer);
     }
 
     @Override
-    public void forEachMember(Consumer<DexMember<?, ?>> consumer) {
-      forEachField(consumer);
-      forEachMethod(consumer);
-    }
-
-    @Override
     public void forEachMember(BiConsumer<DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer) {
       forEachField(consumer);
       forEachMethod(consumer);
     }
 
     @Override
-    public void forEachMethod(Consumer<? super DexMethod> consumer) {
-      methodsWithRules.keySet().forEach(consumer);
-    }
-
-    @Override
     public void forEachMethod(BiConsumer<? super DexMethod, Set<ProguardKeepRuleBase>> consumer) {
       methodsWithRules.forEach(consumer);
     }
-
-    @Override
-    public Set<ProguardKeepRuleBase> getRulesForClass(DexType type) {
-      return classesWithRules.get(type);
-    }
-
-    @Override
-    public Set<ProguardKeepRuleBase> getRulesForField(DexField field) {
-      return fieldsWithRules.get(field);
-    }
-
-    @Override
-    public Set<ProguardKeepRuleBase> getRulesForMethod(DexMethod method) {
-      return methodsWithRules.get(method);
-    }
-
-    public void removeClass(DexType type) {
-      classesWithRules.remove(type);
-    }
-
-    public void removeField(DexField field) {
-      fieldsWithRules.remove(field);
-    }
-
-    public void removeMethod(DexMethod method) {
-      methodsWithRules.remove(method);
-    }
-
-    public void removeReference(DexReference reference) {
-      reference.accept(this::removeClass, this::removeField, this::removeMethod);
-    }
-
-    public int size() {
-      return classesWithRules.size() + fieldsWithRules.size() + methodsWithRules.size();
-    }
   }
 
   public static class RootSet extends RootSetBase {
@@ -1621,7 +1505,7 @@
     public final Set<ProguardIfRule> ifRules;
 
     private RootSet(
-        MutableItemsWithRules noShrinking,
+        Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
         Set<DexReference> noObfuscation,
         ImmutableList<DexReference> reasonAsked,
         ImmutableList<DexReference> checkDiscarded,
@@ -1695,7 +1579,8 @@
       neverClassInline.addAll(consequentRootSet.neverClassInline);
       noObfuscation.addAll(consequentRootSet.noObfuscation);
       if (addNoShrinking) {
-        noShrinking.addAll(consequentRootSet.noShrinking);
+        consequentRootSet.noShrinking.forEach(
+            (type, rules) -> noShrinking.computeIfAbsent(type, k -> new HashSet<>()).addAll(rules));
       }
       addDependentItems(consequentRootSet.dependentNoShrinking);
       consequentRootSet.dependentKeepClassCompatRule.forEach(
@@ -1715,8 +1600,8 @@
     }
 
     public void copy(DexReference original, DexReference rewritten) {
-      if (noShrinking.containsReference(original)) {
-        noShrinking.addReferenceWithRules(rewritten, noShrinking.getRulesForReference(original));
+      if (noShrinking.containsKey(original)) {
+        noShrinking.put(rewritten, noShrinking.get(original));
       }
       if (noObfuscation.contains(original)) {
         noObfuscation.add(rewritten);
@@ -1730,7 +1615,7 @@
     }
 
     public void prune(DexReference reference) {
-      noShrinking.removeReference(reference);
+      noShrinking.remove(reference);
       noObfuscation.remove(reference);
       noSideEffects.remove(reference);
       assumedValues.remove(reference);
@@ -1748,29 +1633,30 @@
         Enqueuer enqueuer) {
       references.removeIf(
           reference -> {
-            if (reference.isDexType()) {
+            if (reference.isDexField()) {
+              DexEncodedField definition = definitions.definitionFor(reference.asDexField());
+              if (definition == null) {
+                return true;
+              }
+              DexClass holder = definitions.definitionFor(definition.holder());
+              if (holder.isProgramClass()) {
+                return !enqueuer.isFieldReferenced(definition);
+              }
+              return !enqueuer.isNonProgramTypeLive(holder);
+            } else if (reference.isDexMethod()) {
+              DexEncodedMethod definition = definitions.definitionFor(reference.asDexMethod());
+              if (definition == null) {
+                return true;
+              }
+              DexClass holder = definitions.definitionFor(definition.holder());
+              if (holder.isProgramClass()) {
+                return !enqueuer.isMethodLive(definition) && !enqueuer.isMethodTargeted(definition);
+              }
+              return !enqueuer.isNonProgramTypeLive(holder);
+            } else {
               DexClass definition = definitions.definitionFor(reference.asDexType());
               return definition == null || !enqueuer.isTypeLive(definition);
             }
-
-            assert reference.isDexMember();
-
-            DexMember<?, ?> member = reference.asDexMember();
-            DexClass holder = definitions.definitionForHolder(member);
-            DexEncodedMember<?, ?> definition = member.lookupOnClass(holder);
-            if (definition == null) {
-              return true;
-            }
-            if (holder.isProgramClass()) {
-              if (definition.isDexEncodedField()) {
-                DexEncodedField field = definition.asDexEncodedField();
-                return !enqueuer.isFieldReferenced(field);
-              }
-              assert definition.isDexEncodedMethod();
-              DexEncodedMethod method = definition.asDexEncodedMethod();
-              return !enqueuer.isMethodLive(method) && !enqueuer.isMethodTargeted(method);
-            }
-            return !enqueuer.isNonProgramTypeLive(holder);
           });
     }
 
@@ -1802,49 +1688,52 @@
     }
 
     public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
-      noShrinking.forEachField(
-          reference -> {
-            DexClass holder = appInfo.definitionForHolder(reference);
-            DexEncodedField field = reference.lookupOnClass(holder);
-            if (field != null
-                && (field.isStatic() || isKeptDirectlyOrIndirectly(field.holder(), appInfo))) {
-              assert appInfo.isFieldRead(field)
-                  : "Expected kept field `" + field.toSourceString() + "` to be read";
-              assert appInfo.isFieldWritten(field)
-                  : "Expected kept field `" + field.toSourceString() + "` to be written";
-            }
-          });
+      for (DexReference reference : noShrinking.keySet()) {
+        if (reference.isDexField()) {
+          DexField field = reference.asDexField();
+          DexEncodedField encodedField = appInfo.definitionFor(field);
+          if (encodedField != null
+              && (encodedField.isStatic() || isKeptDirectlyOrIndirectly(field.holder, appInfo))) {
+            assert appInfo.isFieldRead(encodedField)
+                : "Expected kept field `" + field.toSourceString() + "` to be read";
+            assert appInfo.isFieldWritten(encodedField)
+                : "Expected kept field `" + field.toSourceString() + "` to be written";
+          }
+        }
+      }
       return true;
     }
 
     public boolean verifyKeptMethodsAreTargetedAndLive(AppInfoWithLiveness appInfo) {
-      noShrinking.forEachMethod(
-          reference -> {
-            assert appInfo.targetedMethods.contains(reference)
-                : "Expected kept method `" + reference.toSourceString() + "` to be targeted";
-            DexEncodedMethod method =
-                appInfo.definitionForHolder(reference).lookupMethod(reference);
-            if (!method.isAbstract() && isKeptDirectlyOrIndirectly(method.holder(), appInfo)) {
-              assert appInfo.liveMethods.contains(reference)
-                  : "Expected non-abstract kept method `"
-                      + reference.toSourceString()
-                      + "` to be live";
-            }
-          });
+      for (DexReference reference : noShrinking.keySet()) {
+        if (reference.isDexMethod()) {
+          DexMethod method = reference.asDexMethod();
+          assert appInfo.targetedMethods.contains(method)
+              : "Expected kept method `" + method.toSourceString() + "` to be targeted";
+          DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
+          if (!encodedMethod.accessFlags.isAbstract()
+              && isKeptDirectlyOrIndirectly(method.holder, appInfo)) {
+            assert appInfo.liveMethods.contains(method)
+                : "Expected non-abstract kept method `" + method.toSourceString() + "` to be live";
+          }
+        }
+      }
       return true;
     }
 
     public boolean verifyKeptTypesAreLive(AppInfoWithLiveness appInfo) {
-      noShrinking.forEachClass(
-          type -> {
-            assert appInfo.isLiveProgramType(type)
-                : "Expected kept type `" + type.toSourceString() + "` to be live";
-          });
+      for (DexReference reference : noShrinking.keySet()) {
+        if (reference.isDexType()) {
+          DexType type = reference.asDexType();
+          assert appInfo.isLiveProgramType(type)
+              : "Expected kept type `" + type.toSourceString() + "` to be live";
+        }
+      }
       return true;
     }
 
     private boolean isKeptDirectlyOrIndirectly(DexType type, AppInfoWithLiveness appInfo) {
-      if (noShrinking.containsClass(type)) {
+      if (noShrinking.containsKey(type)) {
         return true;
       }
       DexClass clazz = appInfo.definitionFor(type);
@@ -1859,33 +1748,37 @@
 
     public boolean verifyKeptItemsAreKept(DexApplication application, AppInfo appInfo) {
       // Create a mapping from each required type to the set of required members on that type.
-      Map<DexType, Set<DexMember<?, ?>>> requiredMembersPerType = new IdentityHashMap<>();
-      noShrinking.forEachClass(
-          type -> {
-            assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(type)
-                : "Expected reference `" + type.toSourceString() + "` to be pinned";
-            requiredMembersPerType.computeIfAbsent(type, key -> Sets.newIdentityHashSet());
-          });
-      noShrinking.forEachMember(
-          member -> {
-            assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(member)
-                : "Expected reference `" + member.toSourceString() + "` to be pinned";
-            requiredMembersPerType
-                .computeIfAbsent(member.holder, key -> Sets.newIdentityHashSet())
-                .add(member);
-          });
+      Map<DexType, Set<DexReference>> requiredReferencesPerType = new IdentityHashMap<>();
+      for (DexReference reference : noShrinking.keySet()) {
+        // Check that `pinnedItems` is a super set of the root set.
+        assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(reference)
+            : "Expected reference `" + reference.toSourceString() + "` to be pinned";
+        if (reference.isDexType()) {
+          DexType type = reference.asDexType();
+          requiredReferencesPerType.putIfAbsent(type, Sets.newIdentityHashSet());
+        } else {
+          assert reference.isDexField() || reference.isDexMethod();
+          DexType holder =
+              reference.isDexField()
+                  ? reference.asDexField().holder
+                  : reference.asDexMethod().holder;
+          requiredReferencesPerType
+              .computeIfAbsent(holder, key -> Sets.newIdentityHashSet())
+              .add(reference);
+        }
+      }
 
       // Run through each class in the program and check that it has members it must have.
       for (DexProgramClass clazz : application.classes()) {
-        Set<DexMember<?, ?>> requiredMembers =
-            requiredMembersPerType.getOrDefault(clazz.type, ImmutableSet.of());
+        Set<DexReference> requiredReferences =
+            requiredReferencesPerType.getOrDefault(clazz.type, ImmutableSet.of());
 
         Set<DexField> fields = null;
         Set<DexMethod> methods = null;
 
-        for (DexMember<?, ?> requiredMember : requiredMembers) {
-          if (requiredMember.isDexField()) {
-            DexField requiredField = requiredMember.asDexField();
+        for (DexReference requiredReference : requiredReferences) {
+          if (requiredReference.isDexField()) {
+            DexField requiredField = requiredReference.asDexField();
             if (fields == null) {
               // Create a Set of the fields to avoid quadratic behavior.
               fields =
@@ -1897,8 +1790,8 @@
                 : "Expected field `"
                     + requiredField.toSourceString()
                     + "` from the root set to be present";
-          } else {
-            DexMethod requiredMethod = requiredMember.asDexMethod();
+          } else if (requiredReference.isDexMethod()) {
+            DexMethod requiredMethod = requiredReference.asDexMethod();
             if (methods == null) {
               // Create a Set of the methods to avoid quadratic behavior.
               methods =
@@ -1910,18 +1803,20 @@
                 : "Expected method `"
                     + requiredMethod.toSourceString()
                     + "` from the root set to be present";
+          } else {
+            assert false;
           }
         }
-        requiredMembersPerType.remove(clazz.type);
+        requiredReferencesPerType.remove(clazz.type);
       }
 
       // If the map is non-empty, then a type in the root set was not in the application.
-      if (!requiredMembersPerType.isEmpty()) {
-        DexType type = requiredMembersPerType.keySet().iterator().next();
+      if (!requiredReferencesPerType.isEmpty()) {
+        DexType type = requiredReferencesPerType.keySet().iterator().next();
         DexClass clazz = application.definitionFor(type);
         assert clazz == null || clazz.isProgramClass()
             : "Unexpected library type in root set: `" + type + "`";
-        assert requiredMembersPerType.isEmpty()
+        assert requiredReferencesPerType.isEmpty()
             : "Expected type `" + type.toSourceString() + "` to be present";
       }
 
@@ -1932,6 +1827,7 @@
     public String toString() {
       StringBuilder builder = new StringBuilder();
       builder.append("RootSet");
+
       builder.append("\nnoShrinking: " + noShrinking.size());
       builder.append("\nnoObfuscation: " + noObfuscation.size());
       builder.append("\nreasonAsked: " + reasonAsked.size());
@@ -1941,6 +1837,18 @@
       builder.append("\ndependentNoShrinking: " + dependentNoShrinking.size());
       builder.append("\nidentifierNameStrings: " + identifierNameStrings.size());
       builder.append("\nifRules: " + ifRules.size());
+
+      builder.append("\n\nNo Shrinking:");
+      noShrinking.keySet().stream()
+          .sorted(Comparator.comparing(DexReference::toSourceString))
+          .forEach(
+              a ->
+                  builder
+                      .append("\n")
+                      .append(a.toSourceString())
+                      .append(" ")
+                      .append(noShrinking.get(a)));
+      builder.append("\n");
       return builder.toString();
     }
   }
@@ -1952,7 +1860,7 @@
     ConsequentRootSet(
         Set<DexMethod> neverInline,
         Set<DexType> neverClassInline,
-        MutableItemsWithRules noShrinking,
+        Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
         Set<DexReference> noObfuscation,
         Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 1b69bc7..3dd0f4f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1788,10 +1788,10 @@
     private boolean foundIllegalAccess;
     private ProgramMethod context;
 
-    private final AppView<AppInfoWithLiveness> appView;
+    private final AppView<?> appView;
     private final DexClass source;
 
-    public IllegalAccessDetector(AppView<AppInfoWithLiveness> appView, DexClass source) {
+    public IllegalAccessDetector(AppView<?> appView, DexClass source) {
       super(appView.dexItemFactory());
       this.appView = appView;
       this.source = source;
@@ -1813,7 +1813,7 @@
           checkTypeReference(field.holder);
           checkTypeReference(field.type);
 
-          DexEncodedField definition = appView.appInfo().resolveField(field).getResolvedField();
+          DexEncodedField definition = appView.definitionFor(field);
           if (definition == null || !definition.accessFlags.isPublic()) {
             foundIllegalAccess = true;
           }