Reland "Remove all uses of definitionFor(DexField)"

This reverts commit 39673cbe5607eb29e6f86cdb15d9866d8a6a9157.

Change-Id: I4e1cb3eff48494ea43190b01da02e5e3ea40277f
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 e49f177..3706dec 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -21,9 +21,6 @@
   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;
@@ -33,31 +30,28 @@
   private final BooleanBox obsolete;
 
   public AppInfo(DexApplication application) {
-    this(application, new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new BooleanBox());
+    this(application, new ConcurrentHashMap<>(), new BooleanBox());
   }
 
   // For desugaring.
   protected AppInfo(AppInfo appInfo) {
-    this(appInfo.app, appInfo.fieldDefinitionsCache, appInfo.synthesizedClasses, appInfo.obsolete);
+    this(appInfo.app, 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;
   }
@@ -102,7 +96,6 @@
     assert checkIfObsolete();
     assert clazz.type.isD8R8SynthesizedClassType();
     DexProgramClass previous = synthesizedClasses.put(clazz.type, clazz);
-    invalidateFieldCacheFor(clazz.type);
     assert previous == null || previous == clazz;
   }
 
@@ -183,21 +176,6 @@
     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 33c63dc..b4534f5 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -228,12 +228,6 @@
 
   @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 5055cc5..214e41c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -7,9 +7,6 @@
 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 31017af..84d581e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -7,6 +7,8 @@
 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> {
 
@@ -24,7 +26,28 @@
   }
 
   @Override
-  public <T> void apply(
+  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(
       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 d04808b..02fd431 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -13,6 +13,10 @@
     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 89c8f05..3618578 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -12,6 +12,8 @@
 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> {
 
@@ -30,7 +32,23 @@
   }
 
   @Override
-  public <T> void apply(
+  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(
       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 900712b..0fe1d69 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -133,6 +133,10 @@
     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 3735378..c37c9de 100644
--- a/src/main/java/com/android/tools/r8/graph/DexReference.java
+++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -4,6 +4,7 @@
 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;
@@ -13,7 +14,17 @@
  */
 public abstract class DexReference extends IndexedDexItem {
 
-  public abstract <T> void apply(
+  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(
       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 dfe07f6..fc7ae3e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -31,6 +31,8 @@
 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> {
@@ -116,7 +118,23 @@
   }
 
   @Override
-  public <T> void apply(
+  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(
       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 d66ef7e..3699d2e 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -75,13 +75,6 @@
 
   @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 fbb42a9..01d86a1 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,8 +7,10 @@
 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;
@@ -106,8 +108,9 @@
       }
       AbstractValue abstractValue = root.getAbstractValue(appView, context);
       if (abstractValue.isSingleFieldValue()) {
-        DexEncodedField field =
-            appView.definitionFor(abstractValue.asSingleFieldValue().getField());
+        DexField fieldReference = abstractValue.asSingleFieldValue().getField();
+        DexClass holder = appView.definitionForHolder(fieldReference);
+        DexEncodedField field = fieldReference.lookupOnClass(holder);
         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 a2a932c..9fe885d 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,7 +5,9 @@
 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;
@@ -71,12 +73,14 @@
     assert !isEmpty();
     ConcreteMutableFieldSet rewrittenSet = new ConcreteMutableFieldSet();
     for (DexEncodedField field : fields) {
-      DexEncodedField rewrittenField = appView.definitionFor(lens.lookupField(field.field));
+      DexField rewrittenFieldReference = lens.lookupField(field.field);
+      DexClass holder = appView.definitionForHolder(rewrittenFieldReference);
+      DexEncodedField rewrittenField = rewrittenFieldReference.lookupOnClass(holder);
       if (rewrittenField == null) {
         assert false;
         continue;
       }
-      rewrittenSet.add(field);
+      rewrittenSet.add(rewrittenField);
     }
     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 e66d342..ba49fc0 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,6 +14,7 @@
 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;
@@ -84,16 +85,20 @@
             }
           }
         } else if (instruction.isFieldInstruction()) {
-          DexEncodedField encodedField =
-              appView.definitionFor(instruction.asFieldInstruction().getField());
-          if (encodedField != null && fieldAccessRequiresRewriting(encodedField, method)) {
+          // 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)) {
             if (instruction.isInstanceGet() || instruction.isStaticGet()) {
-              DexMethod bridge = ensureFieldAccessBridge(encodedField, true);
+              DexMethod bridge = ensureFieldAccessBridge(field, true);
               instructions.replaceCurrentInstruction(
                   new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
             } else {
               assert instruction.isInstancePut() || instruction.isStaticPut();
-              DexMethod bridge = ensureFieldAccessBridge(encodedField, false);
+              DexMethod bridge = ensureFieldAccessBridge(field, 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 7da225c..2586141 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,14 +84,15 @@
     return appView.definitionFor(appView.graphLense().lookupType(type));
   }
 
-  private DexEncodedMethod definitionFor(
+  private DexEncodedMethod lookupOnHolder(
       DexMethod method, DexClassAndMethod context, Invoke.Type invokeType) {
     return appView.definitionFor(
         appView.graphLense().lookupMethod(method, context.getReference(), invokeType).getMethod());
   }
 
-  private DexEncodedField definitionFor(DexField field) {
-    return appView.definitionFor(appView.graphLense().lookupField(field));
+  private DexEncodedField lookupOnHolder(DexField field) {
+    DexField rewritten = appView.graphLense().lookupField(field);
+    return rewritten.lookupOnClass(appView.definitionForHolder(rewritten));
   }
 
   // Extract the list of types in the programClass' nest, of host hostClass
@@ -365,7 +366,7 @@
       if (!method.holder.isClassType()) {
         return false;
       }
-      DexEncodedMethod encodedMethod = definitionFor(method, context, invokeType);
+      DexEncodedMethod encodedMethod = lookupOnHolder(method, context, invokeType);
       if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, context)) {
         ensureInvokeBridge(encodedMethod);
         return true;
@@ -374,7 +375,10 @@
     }
 
     private boolean registerFieldAccess(DexField field, boolean isGet) {
-      DexEncodedField encodedField = definitionFor(field);
+      // 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);
       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 f2cefad..def219f 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,10 +2537,13 @@
                 if (singleFieldValue.getField() == otherSingleFieldValue.getField()) {
                   simplifyIfWithKnownCondition(code, block, theIf, 0);
                 } else {
-                  DexEncodedField field = appView.definitionFor(singleFieldValue.getField());
+                  DexClass holder = appView.definitionForHolder(singleFieldValue.getField());
+                  DexEncodedField field = singleFieldValue.getField().lookupOnClass(holder);
                   if (field != null && field.isEnum()) {
+                    DexClass otherHolder =
+                        appView.definitionForHolder(otherSingleFieldValue.getField());
                     DexEncodedField otherField =
-                        appView.definitionFor(otherSingleFieldValue.getField());
+                        otherSingleFieldValue.getField().lookupOnClass(otherHolder);
                     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 ef81097..5178020 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 appView.definitionFor(field);
+      return method.getHolder().lookupField(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 2fe8a19..fe70918 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,7 +4,9 @@
 
 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;
@@ -32,9 +34,10 @@
       BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
     infos.forEach(
         (field, info) -> {
-          DexEncodedField encodedField = definitions.definitionFor(field);
-          if (encodedField != null) {
-            consumer.accept(encodedField, info);
+          DexClass holder = definitions.definitionForHolder(field);
+          DexEncodedField definition = field.lookupOnClass(holder);
+          if (definition != null) {
+            consumer.accept(definition, 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 ad6313e..bcea380 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 = appView.definitionFor(field);
+            DexEncodedField definition = field.lookupOnClass(appView.definitionForHolder(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 f3fba2d..c326f61 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -12,9 +12,7 @@
 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;
@@ -122,11 +120,6 @@
     }
 
     @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 8cffa05..b014c4f 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -3,15 +3,19 @@
 // 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;
@@ -178,9 +182,7 @@
                   getOrCreateReservedFieldNamingState(clazz.type);
               FieldNamingState state = parentState.createChildState(reservedNames);
               if (clazz.isProgramClass()) {
-                for (DexEncodedField field : clazz.fields()) {
-                  renameField(field, state);
-                }
+                clazz.asProgramClass().forEachProgramField(field -> renameField(field, state));
               }
 
               assert !states.containsKey(clazz.type);
@@ -212,11 +214,14 @@
     for (DexClass clazz : partition) {
       if (clazz.isProgramClass()) {
         assert clazz.isInterface();
-        for (DexEncodedField field : clazz.fields()) {
-          DexString newName = renameField(field, state);
-          namesToBeReservedInImplementsSubclasses.markReservedDirectly(
-              newName, field.field.name, field.field.type);
-        }
+        clazz
+            .asProgramClass()
+            .forEachProgramField(
+                field -> {
+                  DexString newName = renameField(field, state);
+                  namesToBeReservedInImplementsSubclasses.markReservedDirectly(
+                      newName, field.getReference().name, field.getReference().type);
+                });
       }
     }
 
@@ -236,11 +241,10 @@
     }
   }
 
-  private DexString renameField(DexEncodedField encodedField, FieldNamingState state) {
-    DexField field = encodedField.field;
+  private DexString renameField(ProgramField field, FieldNamingState state) {
     DexString newName = state.getOrCreateNameFor(field);
-    if (newName != field.name) {
-      renaming.put(field, newName);
+    if (newName != field.getReference().name) {
+      renaming.put(field.getReference(), newName);
     }
     return newName;
   }
@@ -256,32 +260,17 @@
   }
 
   private void renameNonReboundAccessToField(DexField field) {
-    // Already renamed
+    // If the given field reference is a non-rebound reference to a program field, then assign the
+    // same name as the resolved field.
     if (renaming.containsKey(field)) {
       return;
     }
-    DexEncodedField definition = appView.definitionFor(field);
-    if (definition != null) {
-      assert definition.field == field;
+    DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(field));
+    if (holder == null) {
       return;
     }
-    // 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.
+    DexEncodedField definition = appView.appInfo().resolveFieldOn(holder, field).getResolvedField();
+    if (definition != null && definition.field != field && renaming.containsKey(definition.field)) {
       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 f8d80cb..35e3d3f 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -6,11 +6,9 @@
 
 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;
@@ -20,7 +18,7 @@
 
   private final ReservedFieldNamingState reservedNames;
   private final MemberNamingStrategy strategy;
-  private final BiPredicate<DexString, DexField> isAvailable;
+  private final BiPredicate<DexString, ProgramField> isAvailable;
 
   public FieldNamingState(
       AppView<? extends AppInfoWithClassHierarchy> appView, MemberNamingStrategy strategy) {
@@ -42,7 +40,8 @@
     super(appView, internalStates);
     this.reservedNames = reservedNames;
     this.strategy = strategy;
-    this.isAvailable = (newName, field) -> !reservedNames.isReserved(newName, field.type);
+    this.isAvailable =
+        (newName, field) -> !reservedNames.isReserved(newName, field.getReference().type);
   }
 
   public FieldNamingState createChildState(ReservedFieldNamingState reservedNames) {
@@ -52,20 +51,13 @@
     return childState;
   }
 
-  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;
-      }
+  public DexString getOrCreateNameFor(ProgramField field) {
+    DexString reservedName = strategy.getReservedName(field.getDefinition(), field.getHolder());
+    if (reservedName != null) {
+      return reservedName;
     }
     // TODO(b/133208730) If we cannot resolve the field, are we then allowed to rename it?
-    return getOrCreateInternalState(field).createNewName(field);
+    return getOrCreateInternalState(field.getReference()).createNewName(field);
   }
 
   public void includeReservations(ReservedFieldNamingState reservedNames) {
@@ -100,9 +92,9 @@
       this.nextNameIndex = nextNameIndex;
     }
 
-    public DexString createNewName(DexField field) {
+    public DexString createNewName(ProgramField field) {
       DexString name = strategy.next(field, this, isAvailable);
-      assert !reservedNames.isReserved(name, field.type);
+      assert !reservedNames.isReserved(name, field.getReference().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 bacc7e0..cddbea8 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(
-      DexField field,
+      ProgramField field,
       InternalNamingState internalState,
-      BiPredicate<DexString, DexField> isAvailable);
+      BiPredicate<DexString, ProgramField> 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 8f1a41a..d0f0381 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(
-        DexField field,
+        ProgramField field,
         InternalNamingState internalState,
-        BiPredicate<DexString, DexField> isAvailable) {
-      assert checkAllowMemberRenaming(field.holder);
+        BiPredicate<DexString, ProgramField> isAvailable) {
+      assert checkAllowMemberRenaming(field.getHolderType());
       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 3ce8e7f..e720b30 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -16,6 +16,7 @@
 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;
@@ -32,7 +33,6 @@
 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,8 +301,9 @@
       DexField originalField = ((FieldSignature) signature).toDexField(factory, type);
       addMemberNaming(
           originalField, memberNaming, addToAdditionalMaps ? additionalFieldNamings : null);
-      DexEncodedField encodedField = appView.definitionFor(originalField);
-      if (encodedField == null || !encodedField.accessFlags.isPrivate()) {
+      DexClass holder = appView.definitionForHolder(originalField);
+      DexEncodedField field = originalField.lookupOnClass(holder);
+      if (field == null || !field.isPrivate()) {
         nonPrivateMembers.put(originalField, memberNaming);
       }
     }
@@ -465,62 +466,46 @@
 
     @Override
     public DexString next(
-        DexMethod method,
+        DexMethod reference,
         InternalNamingState internalState,
         BiPredicate<DexString, DexMethod> isAvailable) {
-      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();
+      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();
       return nextName;
     }
 
     @Override
     public DexString next(
-        DexField field,
+        ProgramField field,
         InternalNamingState internalState,
-        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);
+        BiPredicate<DexString, ProgramField> isAvailable) {
+      DexField reference = field.getReference();
+      DexString reservedName =
+          getReservedName(field.getDefinition(), reference.name, field.getHolder());
       if (reservedName != null) {
-        if (!isAvailable.test(reservedName, reference)) {
-          reportReservationError(definition.toReference(), reservedName);
+        if (!isAvailable.test(reservedName, field)) {
+          reportReservationError(reference, reservedName);
         }
         return reservedName;
       }
       assert !mappedNames.containsKey(reference);
       assert appView.rootSet().mayBeMinified(reference, appView);
-      return generateName.apply(reference, internalState, isAvailable);
+      return super.next(field, 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 df5a09c..9dfa92e 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -26,6 +26,7 @@
 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;
@@ -975,8 +976,9 @@
     // Switchmap classes should never be affected by renaming.
     assert lens.assertDefinitionsNotModified(
         switchMaps.keySet().stream()
-            .map(this::definitionFor)
-            .filter(Objects::nonNull)
+            .map(this::resolveField)
+            .filter(FieldResolutionResult::isSuccessfulResolution)
+            .map(FieldResolutionResult::getResolvedField)
             .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 8329ac7..ff991cf 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -504,14 +504,6 @@
     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) {
@@ -3349,7 +3341,7 @@
     ProgramMethod methodToKeep = action.getMethodToKeep();
     ProgramMethod singleTarget = action.getSingleTarget();
     DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
-    if (rootSet.noShrinking.containsKey(singleTargetMethod.method)) {
+    if (rootSet.noShrinking.containsMethod(singleTarget.getReference())) {
       return;
     }
     if (methodToKeep != singleTarget) {
@@ -4171,12 +4163,6 @@
 
     @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 3bbfa9b..5b3e34f 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -55,7 +55,6 @@
 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;
@@ -82,7 +81,7 @@
   private final SubtypingInfo subtypingInfo;
   private final DirectMappedDexApplication application;
   private final Iterable<? extends ProguardConfigurationRule> rules;
-  private final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking = new IdentityHashMap<>();
+  private final MutableItemsWithRules noShrinking = new MutableItemsWithRules();
   private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
   private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
   private final LinkedHashMap<DexReference, DexReference> checkDiscarded = new LinkedHashMap<>();
@@ -1100,7 +1099,7 @@
               .computeIfAbsent(precondition.toReference(), x -> new MutableItemsWithRules())
               .addReferenceWithRule(item.toReference(), keepRule);
         } else {
-          noShrinking.computeIfAbsent(item.toReference(), i -> new HashSet<>()).add(keepRule);
+          noShrinking.addReferenceWithRule(item.toReference(), keepRule);
         }
         context.markAsUsed();
       }
@@ -1268,7 +1267,7 @@
 
     final Set<DexMethod> neverInline;
     final Set<DexType> neverClassInline;
-    final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking;
+    final MutableItemsWithRules noShrinking;
     final Set<DexReference> noObfuscation;
     final Map<DexReference, MutableItemsWithRules> dependentNoShrinking;
     final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
@@ -1277,7 +1276,7 @@
     RootSetBase(
         Set<DexMethod> neverInline,
         Set<DexType> neverClassInline,
-        Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
+        MutableItemsWithRules noShrinking,
         Set<DexReference> noObfuscation,
         Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
@@ -1403,16 +1402,45 @@
       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 {
@@ -1439,48 +1467,159 @@
     }
 
     public void addAll(ItemsWithRules items) {
-      items.forEachClass(classesWithRules::put);
-      items.forEachField(fieldsWithRules::put);
-      items.forEachMethod(methodsWithRules::put);
+      items.forEachClass(this::addClassWithRules);
+      items.forEachField(this::addFieldWithRules);
+      items.forEachMethod(this::addMethodWithRules);
     }
 
     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.apply(
+      reference.accept(
           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 void putAll(ItemsWithRules items) {
+      items.forEachClass(this::putClassWithRules);
+      items.forEachField(this::putFieldWithRules);
+      items.forEachMethod(this::putMethodWithRules);
+    }
+
+    public void putClassWithRules(DexType type, Set<ProguardKeepRuleBase> rules) {
+      classesWithRules.put(type, rules);
+    }
+
+    public void putFieldWithRules(DexField field, Set<ProguardKeepRuleBase> rules) {
+      fieldsWithRules.put(field, rules);
+    }
+
+    public void putMethodWithRules(DexMethod method, Set<ProguardKeepRuleBase> rules) {
+      methodsWithRules.put(method, rules);
+    }
+
+    public void putReferenceWithRules(DexReference reference, Set<ProguardKeepRuleBase> rules) {
+      reference.accept(
+          this::putClassWithRules, this::putFieldWithRules, this::putMethodWithRules, rules);
+    }
+
+    public int size() {
+      return classesWithRules.size() + fieldsWithRules.size() + methodsWithRules.size();
+    }
   }
 
   public static class RootSet extends RootSetBase {
@@ -1505,7 +1644,7 @@
     public final Set<ProguardIfRule> ifRules;
 
     private RootSet(
-        Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
+        MutableItemsWithRules noShrinking,
         Set<DexReference> noObfuscation,
         ImmutableList<DexReference> reasonAsked,
         ImmutableList<DexReference> checkDiscarded,
@@ -1579,8 +1718,7 @@
       neverClassInline.addAll(consequentRootSet.neverClassInline);
       noObfuscation.addAll(consequentRootSet.noObfuscation);
       if (addNoShrinking) {
-        consequentRootSet.noShrinking.forEach(
-            (type, rules) -> noShrinking.computeIfAbsent(type, k -> new HashSet<>()).addAll(rules));
+        noShrinking.addAll(consequentRootSet.noShrinking);
       }
       addDependentItems(consequentRootSet.dependentNoShrinking);
       consequentRootSet.dependentKeepClassCompatRule.forEach(
@@ -1596,12 +1734,12 @@
           (reference, dependence) ->
               dependentNoShrinking
                   .computeIfAbsent(reference, x -> new MutableItemsWithRules())
-                  .addAll(dependence));
+                  .putAll(dependence));
     }
 
     public void copy(DexReference original, DexReference rewritten) {
-      if (noShrinking.containsKey(original)) {
-        noShrinking.put(rewritten, noShrinking.get(original));
+      if (noShrinking.containsReference(original)) {
+        noShrinking.putReferenceWithRules(rewritten, noShrinking.getRulesForReference(original));
       }
       if (noObfuscation.contains(original)) {
         noObfuscation.add(rewritten);
@@ -1615,7 +1753,7 @@
     }
 
     public void prune(DexReference reference) {
-      noShrinking.remove(reference);
+      noShrinking.removeReference(reference);
       noObfuscation.remove(reference);
       noSideEffects.remove(reference);
       assumedValues.remove(reference);
@@ -1633,30 +1771,29 @@
         Enqueuer enqueuer) {
       references.removeIf(
           reference -> {
-            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 {
+            if (reference.isDexType()) {
               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);
           });
     }
 
@@ -1688,52 +1825,49 @@
     }
 
     public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
-      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";
-          }
-        }
-      }
+      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";
+            }
+          });
       return true;
     }
 
     public boolean verifyKeptMethodsAreTargetedAndLive(AppInfoWithLiveness appInfo) {
-      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";
-          }
-        }
-      }
+      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";
+            }
+          });
       return true;
     }
 
     public boolean verifyKeptTypesAreLive(AppInfoWithLiveness appInfo) {
-      for (DexReference reference : noShrinking.keySet()) {
-        if (reference.isDexType()) {
-          DexType type = reference.asDexType();
-          assert appInfo.isLiveProgramType(type)
-              : "Expected kept type `" + type.toSourceString() + "` to be live";
-        }
-      }
+      noShrinking.forEachClass(
+          type -> {
+            assert appInfo.isLiveProgramType(type)
+                : "Expected kept type `" + type.toSourceString() + "` to be live";
+          });
       return true;
     }
 
     private boolean isKeptDirectlyOrIndirectly(DexType type, AppInfoWithLiveness appInfo) {
-      if (noShrinking.containsKey(type)) {
+      if (noShrinking.containsClass(type)) {
         return true;
       }
       DexClass clazz = appInfo.definitionFor(type);
@@ -1748,37 +1882,33 @@
 
     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<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);
-        }
-      }
+      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);
+          });
 
       // Run through each class in the program and check that it has members it must have.
       for (DexProgramClass clazz : application.classes()) {
-        Set<DexReference> requiredReferences =
-            requiredReferencesPerType.getOrDefault(clazz.type, ImmutableSet.of());
+        Set<DexMember<?, ?>> requiredMembers =
+            requiredMembersPerType.getOrDefault(clazz.type, ImmutableSet.of());
 
         Set<DexField> fields = null;
         Set<DexMethod> methods = null;
 
-        for (DexReference requiredReference : requiredReferences) {
-          if (requiredReference.isDexField()) {
-            DexField requiredField = requiredReference.asDexField();
+        for (DexMember<?, ?> requiredMember : requiredMembers) {
+          if (requiredMember.isDexField()) {
+            DexField requiredField = requiredMember.asDexField();
             if (fields == null) {
               // Create a Set of the fields to avoid quadratic behavior.
               fields =
@@ -1790,8 +1920,8 @@
                 : "Expected field `"
                     + requiredField.toSourceString()
                     + "` from the root set to be present";
-          } else if (requiredReference.isDexMethod()) {
-            DexMethod requiredMethod = requiredReference.asDexMethod();
+          } else {
+            DexMethod requiredMethod = requiredMember.asDexMethod();
             if (methods == null) {
               // Create a Set of the methods to avoid quadratic behavior.
               methods =
@@ -1803,20 +1933,18 @@
                 : "Expected method `"
                     + requiredMethod.toSourceString()
                     + "` from the root set to be present";
-          } else {
-            assert false;
           }
         }
-        requiredReferencesPerType.remove(clazz.type);
+        requiredMembersPerType.remove(clazz.type);
       }
 
       // If the map is non-empty, then a type in the root set was not in the application.
-      if (!requiredReferencesPerType.isEmpty()) {
-        DexType type = requiredReferencesPerType.keySet().iterator().next();
+      if (!requiredMembersPerType.isEmpty()) {
+        DexType type = requiredMembersPerType.keySet().iterator().next();
         DexClass clazz = application.definitionFor(type);
         assert clazz == null || clazz.isProgramClass()
             : "Unexpected library type in root set: `" + type + "`";
-        assert requiredReferencesPerType.isEmpty()
+        assert requiredMembersPerType.isEmpty()
             : "Expected type `" + type.toSourceString() + "` to be present";
       }
 
@@ -1827,7 +1955,6 @@
     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());
@@ -1837,18 +1964,6 @@
       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();
     }
   }
@@ -1860,7 +1975,7 @@
     ConsequentRootSet(
         Set<DexMethod> neverInline,
         Set<DexType> neverClassInline,
-        Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
+        MutableItemsWithRules 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 3dd0f4f..1b69bc7 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<?> appView;
+    private final AppView<AppInfoWithLiveness> appView;
     private final DexClass source;
 
-    public IllegalAccessDetector(AppView<?> appView, DexClass source) {
+    public IllegalAccessDetector(AppView<AppInfoWithLiveness> 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.definitionFor(field);
+          DexEncodedField definition = appView.appInfo().resolveField(field).getResolvedField();
           if (definition == null || !definition.accessFlags.isPublic()) {
             foundIllegalAccess = true;
           }