Introduce EnumValueInfoMap rewritten with lens.

Bug: 150193407
Change-Id: Id50ef36b7b4a38cd110bcfae1690718f5de52d00
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 65bf330..b0adc79 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -42,7 +42,7 @@
 import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.UninstantiatedTypeOptimizationGraphLense;
 import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector;
 import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector.UnusedArgumentsGraphLense;
-import com.android.tools.r8.ir.optimize.enums.EnumInfoMapCollector;
+import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.kotlin.Kotlin;
@@ -516,7 +516,7 @@
         appViewWithLiveness.setAppInfo(new SwitchMapCollector(appViewWithLiveness).run());
       }
       if (options.enableEnumValueOptimization || options.enableEnumUnboxing) {
-        appViewWithLiveness.setAppInfo(new EnumInfoMapCollector(appViewWithLiveness).run());
+        appViewWithLiveness.setAppInfo(new EnumValueInfoMapCollector(appViewWithLiveness).run());
       }
 
       appView.setAppServices(appView.appServices().rewrittenWithLens(appView.graphLense()));
diff --git a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
new file mode 100644
index 0000000..470685f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import java.util.Set;
+
+public class EnumValueInfoMapCollection {
+
+  public static EnumValueInfoMapCollection empty() {
+    return new EnumValueInfoMapCollection(ImmutableMap.of());
+  }
+
+  private final Map<DexType, EnumValueInfoMap> maps;
+
+  private EnumValueInfoMapCollection(Map<DexType, EnumValueInfoMap> maps) {
+    this.maps = maps;
+  }
+
+  public EnumValueInfoMap getEnumValueInfoMap(DexType type) {
+    return maps.get(type);
+  }
+
+  public boolean isEmpty() {
+    return maps.isEmpty();
+  }
+
+  public boolean containsEnum(DexType type) {
+    return maps.containsKey(type);
+  }
+
+  public Set<DexType> enumSet() {
+    return maps.keySet();
+  }
+
+  public EnumValueInfoMapCollection rewrittenWithLens(GraphLense lens) {
+    Builder builder = builder();
+    maps.forEach(
+        (type, map) -> {
+          DexType dexType = lens.lookupType(type);
+          // Enum unboxing may have changed the type to int type.
+          // Do not keep the map for such enums.
+          if (!dexType.isPrimitiveType()) {
+            builder.put(dexType, map.rewrittenWithLens(lens));
+          }
+        });
+    return builder.build();
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private ImmutableMap.Builder<DexType, EnumValueInfoMap> builder;
+
+    public Builder put(DexType type, EnumValueInfoMap map) {
+      if (builder == null) {
+        builder = ImmutableMap.builder();
+      }
+      builder.put(type, map);
+      return this;
+    }
+
+    public EnumValueInfoMapCollection build() {
+      if (builder == null) {
+        return empty();
+      }
+      return new EnumValueInfoMapCollection(builder.build());
+    }
+  }
+
+  public static final class EnumValueInfoMap {
+
+    private final Map<DexField, EnumValueInfo> map;
+
+    public EnumValueInfoMap(Map<DexField, EnumValueInfo> map) {
+      this.map = ImmutableMap.copyOf(map);
+    }
+
+    public int size() {
+      return map.size();
+    }
+
+    public EnumValueInfo getEnumValueInfo(DexField field) {
+      return map.get(field);
+    }
+
+    EnumValueInfoMap rewrittenWithLens(GraphLense lens) {
+      ImmutableMap.Builder<DexField, EnumValueInfo> builder = ImmutableMap.builder();
+      map.forEach(
+          (field, valueInfo) ->
+              builder.put(lens.lookupField(field), valueInfo.rewrittenWithLens(lens)));
+      return new EnumValueInfoMap(builder.build());
+    }
+  }
+
+  public static final class EnumValueInfo {
+
+    // The anonymous subtype of this specific value or the enum type.
+    public final DexType type;
+    public final int ordinal;
+
+    public EnumValueInfo(DexType type, int ordinal) {
+      this.type = type;
+      this.ordinal = ordinal;
+    }
+
+    EnumValueInfo rewrittenWithLens(GraphLense lens) {
+      DexType newType = lens.lookupType(type);
+      if (type == newType) {
+        return this;
+      }
+      return new EnumValueInfo(newType, ordinal);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 11bf08e..8588a9d 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableMap;
@@ -328,20 +327,6 @@
     return builder.build();
   }
 
-  // TODO(b/150193407): Move to enumInfoMap and rewrite fields.
-  public static ImmutableMap<DexType, Map<DexField, EnumValueInfo>> rewriteEnumValueInfoMaps(
-      Map<DexType, Map<DexField, EnumValueInfo>> original, GraphLense lens) {
-    ImmutableMap.Builder<DexType, Map<DexField, EnumValueInfo>> builder = ImmutableMap.builder();
-    original.forEach(
-        (enumType, map) -> {
-          DexType dexType = lens.lookupType(enumType);
-          if (!dexType.isPrimitiveType()) {
-            builder.put(dexType, map);
-          }
-        });
-    return builder.build();
-  }
-
   public boolean verifyMappingToOriginalProgram(
       Iterable<DexProgramClass> classes,
       DexApplication originalApplication,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
index 2c8e6b9..d5d51e1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -81,7 +81,7 @@
       processClasses(clazz);
     }
     if (!switchMaps.isEmpty()) {
-      return appView.appInfo().addSwitchMaps(switchMaps);
+      return appView.appInfo().withSwitchMaps(switchMaps);
     }
     return appView.appInfo();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 6c95511..23fa539 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
@@ -38,7 +39,6 @@
 import com.android.tools.r8.ir.conversion.PostMethodProcessor;
 import com.android.tools.r8.ir.conversion.PostOptimization;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -273,13 +273,13 @@
         continue;
       }
 
-      Map<DexField, EnumValueInfo> enumValueInfoMapFor =
-          appView.appInfo().withLiveness().getEnumValueInfoMapFor(enumClass.type);
-      if (enumValueInfoMapFor == null) {
+      EnumValueInfoMap enumValueInfoMap =
+          appView.appInfo().withLiveness().getEnumValueInfoMap(enumClass.type);
+      if (enumValueInfoMap == null) {
         markEnumAsUnboxable(Reason.MISSING_INFO_MAP, enumClass);
         continue;
       }
-      if (enumValueInfoMapFor.size() != enumClass.staticFields().size() - 1) {
+      if (enumValueInfoMap.size() != enumClass.staticFields().size() - 1) {
         markEnumAsUnboxable(Reason.UNEXPECTED_STATIC_FIELD, enumClass);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 1626d7f..264d61f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -12,12 +12,14 @@
 import com.android.tools.r8.graph.DexApplication;
 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;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
@@ -35,13 +37,10 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -53,8 +52,7 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final DexItemFactory factory;
-  // TODO(b/150193407): Use enumInfoMap instead of Map<DexField, EnumValueInfo>.
-  private final Map<DexType, Map<DexField, EnumValueInfo>> enumsToUnbox;
+  private final EnumValueInfoMapCollection enumsToUnbox;
 
   private final DexType utilityClassType;
   private final DexMethod ordinalUtilityMethod;
@@ -64,9 +62,9 @@
   EnumUnboxingRewriter(AppView<AppInfoWithLiveness> appView, Set<DexType> enumsToUnbox) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
-    ImmutableMap.Builder<DexType, Map<DexField, EnumValueInfo>> builder = ImmutableMap.builder();
+    EnumValueInfoMapCollection.Builder builder = EnumValueInfoMapCollection.builder();
     for (DexType toUnbox : enumsToUnbox) {
-      builder.put(toUnbox, appView.appInfo().withLiveness().getEnumValueInfoMapFor(toUnbox));
+      builder.put(toUnbox, appView.appInfo().withLiveness().getEnumValueInfoMap(toUnbox));
     }
     this.enumsToUnbox = builder.build();
 
@@ -108,15 +106,15 @@
       if (instruction.isStaticGet()) {
         StaticGet staticGet = instruction.asStaticGet();
         DexType holder = staticGet.getField().holder;
-        if (enumsToUnbox.containsKey(holder)) {
+        if (enumsToUnbox.containsEnum(holder)) {
           if (staticGet.outValue() == null) {
             iterator.removeInstructionIgnoreOutValue();
             continue;
           }
-          Map<DexField, EnumValueInfo> enumValueInfoMap = enumsToUnbox.get(holder);
+          EnumValueInfoMap enumValueInfoMap = enumsToUnbox.getEnumValueInfoMap(holder);
           assert enumValueInfoMap != null;
           // Replace by ordinal + 1 for null check (null is 0).
-          EnumValueInfo enumValueInfo = enumValueInfoMap.get(staticGet.getField());
+          EnumValueInfo enumValueInfo = enumValueInfoMap.getEnumValueInfo(staticGet.getField());
           assert enumValueInfo != null
               : "Invalid read to " + staticGet.getField().name + ", error during enum analysis";
           instruction = new ConstNumber(staticGet.outValue(), enumValueInfo.ordinal + 1);
@@ -142,12 +140,12 @@
     }
     TypeLatticeElement typeLattice = instruction.outValue().getTypeLattice();
     assert !typeLattice.isClassType()
-        || !enumsToUnbox.containsKey(typeLattice.asClassTypeLatticeElement().getClassType());
+        || !enumsToUnbox.containsEnum(typeLattice.asClassTypeLatticeElement().getClassType());
     if (typeLattice.isArrayType()) {
       TypeLatticeElement arrayBaseTypeLattice =
           typeLattice.asArrayTypeLatticeElement().getArrayBaseTypeLattice();
       assert !arrayBaseTypeLattice.isClassType()
-          || !enumsToUnbox.containsKey(
+          || !enumsToUnbox.containsEnum(
               arrayBaseTypeLattice.asClassTypeLatticeElement().getClassType());
     }
     return true;
@@ -195,7 +193,7 @@
 
   // TODO(b/150178516): Add a test for this case.
   private boolean utilityClassInMainDexList() {
-    for (DexType toUnbox : enumsToUnbox.keySet()) {
+    for (DexType toUnbox : enumsToUnbox.enumSet()) {
       if (appView.appInfo().isInMainDexList(toUnbox)) {
         return true;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInfoMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInfoMapCollector.java
rename to src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java
index e0991e0..386a32d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInfoMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java
@@ -8,31 +8,34 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
 import java.util.IdentityHashMap;
 import java.util.Map;
 
 /**
  * Extracts the ordinal values and any anonymous subtypes for all Enum classes from their static
  * initializer.
- * <p>
- * An Enum class has a field for each value. In the class initializer, each field is initialized
- * to a singleton object that represents the value. This code matches on the corresponding call
- * to the constructor (instance initializer) and extracts the value of the second argument, which
- * is the ordinal and the holder which is the concrete type.
+ *
+ * <p>An Enum class has a field for each value. In the class initializer, each field is initialized
+ * to a singleton object that represents the value. This code matches on the corresponding call to
+ * the constructor (instance initializer) and extracts the value of the second argument, which is
+ * the ordinal and the holder which is the concrete type.
  */
-public class EnumInfoMapCollector {
+public class EnumValueInfoMapCollector {
 
   private final AppView<AppInfoWithLiveness> appView;
 
-  private final Map<DexType, Map<DexField, EnumValueInfo>> valueInfoMaps = new IdentityHashMap<>();
+  private final EnumValueInfoMapCollection.Builder valueInfoMapsBuilder =
+      EnumValueInfoMapCollection.builder();
 
-  public EnumInfoMapCollector(AppView<AppInfoWithLiveness> appView) {
+  public EnumValueInfoMapCollector(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
@@ -40,8 +43,9 @@
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       processClasses(clazz);
     }
+    EnumValueInfoMapCollection valueInfoMaps = valueInfoMapsBuilder.build();
     if (!valueInfoMaps.isEmpty()) {
-      return appView.appInfo().addEnumValueInfoMaps(valueInfoMaps);
+      return appView.appInfo().withEnumValueInfoMaps(valueInfoMaps);
     }
     return appView.appInfo();
   }
@@ -53,7 +57,7 @@
     }
     DexEncodedMethod initializer = clazz.getClassInitializer();
     IRCode code = initializer.getCode().buildIR(initializer, appView, clazz.origin);
-    Map<DexField, EnumValueInfo> valueInfoMap = new IdentityHashMap<>();
+    Map<DexField, EnumValueInfo> enumValueInfoMap = new IdentityHashMap<>();
     for (Instruction insn : code.instructions()) {
       if (!insn.isStaticPut()) {
         continue;
@@ -86,10 +90,10 @@
       }
 
       EnumValueInfo info = new EnumValueInfo(type, ordinal.asConstNumber().getIntValue());
-      if (valueInfoMap.put(staticPut.getField(), info) != null) {
+      if (enumValueInfoMap.put(staticPut.getField(), info) != null) {
         return;
       }
     }
-    valueInfoMaps.put(clazz.type, valueInfoMap);
+    valueInfoMapsBuilder.put(clazz.type, new EnumValueInfoMap(enumValueInfoMap));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index ee749f0..ddf697d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -11,6 +11,8 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.ir.code.ArrayGet;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
@@ -27,12 +29,10 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.SwitchMapCollector;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.util.Arrays;
-import java.util.Map;
 
 public class EnumValueOptimizer {
 
@@ -76,8 +76,8 @@
       }
       DexField enumField = definition.asStaticGet().getField();
 
-      Map<DexField, EnumValueInfo> valueInfoMap =
-          appView.appInfo().withLiveness().getEnumValueInfoMapFor(enumField.type);
+      EnumValueInfoMap valueInfoMap =
+          appView.appInfo().withLiveness().getEnumValueInfoMap(enumField.type);
       if (valueInfoMap == null) {
         continue;
       }
@@ -86,7 +86,7 @@
       // that it is a static-get to a field whose type is the same as the enclosing class (which
       // is known to be an enum type). An enum may still define a static field using the enum type
       // so ensure the field is present in the ordinal map for final validation.
-      EnumValueInfo valueInfo = valueInfoMap.get(enumField);
+      EnumValueInfo valueInfo = valueInfoMap.getEnumValueInfo(enumField);
       if (valueInfo == null) {
         continue;
       }
@@ -153,9 +153,8 @@
       Int2IntMap targetMap = new Int2IntArrayMap();
       for (int i = 0; i < switchInsn.numberOfKeys(); i++) {
         assert switchInsn.targetBlockIndices()[i] != switchInsn.getFallthroughBlockIndex();
-        int key = switchInsn.getKey(i);
-        DexField field = info.indexMap.get(key);
-        EnumValueInfo valueInfo = info.valueInfoMap.get(field);
+        DexField field = info.indexMap.get(switchInsn.getKey(i));
+        EnumValueInfo valueInfo = info.valueInfoMap.getEnumValueInfo(field);
         targetMap.put(valueInfo.ordinal, switchInsn.targetBlockIndices()[i]);
       }
       int[] keys = targetMap.keySet().toIntArray();
@@ -192,7 +191,7 @@
     final Instruction arrayGet;
     public final Instruction staticGet;
     final Int2ReferenceMap<DexField> indexMap;
-    final Map<DexField, EnumValueInfo> valueInfoMap;
+    final EnumValueInfoMap valueInfoMap;
 
     private EnumSwitchInfo(
         DexType enumClass,
@@ -200,7 +199,7 @@
         Instruction arrayGet,
         Instruction staticGet,
         Int2ReferenceMap<DexField> indexMap,
-        Map<DexField, EnumValueInfo> valueInfoMap) {
+        EnumValueInfoMap valueInfoMap) {
       this.enumClass = enumClass;
       this.ordinalInvoke = ordinalInvoke;
       this.arrayGet = arrayGet;
@@ -223,8 +222,8 @@
    *
    * </blockquote>
    *
-   * and extracts the components and the index and ordinal maps. See {@link EnumInfoMapCollector}
-   * and {@link SwitchMapCollector} for details.
+   * and extracts the components and the index and ordinal maps. See {@link
+   * EnumValueInfoMapCollector} and {@link SwitchMapCollector} for details.
    */
   private EnumSwitchInfo analyzeSwitchOverEnum(IntSwitch switchInsn) {
     Instruction input = switchInsn.inValues().get(0).definition;
@@ -253,7 +252,7 @@
       return null;
     }
     StaticGet staticGet = array.asStaticGet();
-    Int2ReferenceMap<DexField> indexMap = appView.appInfo().getSwitchMapFor(staticGet.getField());
+    Int2ReferenceMap<DexField> indexMap = appView.appInfo().getSwitchMap(staticGet.getField());
     if (indexMap == null || indexMap.isEmpty()) {
       return null;
     }
@@ -264,7 +263,7 @@
     }
     // Due to member rebinding, only the fields are certain to provide the actual enums class.
     DexType enumType = indexMap.values().iterator().next().holder;
-    Map<DexField, EnumValueInfo> valueInfoMap = appView.appInfo().getEnumValueInfoMapFor(enumType);
+    EnumValueInfoMap valueInfoMap = appView.appInfo().getEnumValueInfoMap(enumType);
     if (valueInfoMap == null) {
       return null;
     }
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 6e5899d..a1277ef 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.shaking;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.graph.GraphLense.rewriteEnumValueInfoMaps;
 import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -21,6 +20,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
@@ -175,18 +176,7 @@
   /** A map from switchmap class types to their corresponding switchmaps. */
   final Map<DexField, Int2ReferenceMap<DexField>> switchMaps;
   /** A map from enum types to their value types and ordinals. */
-  final Map<DexType, Map<DexField, EnumValueInfo>> enumValueInfoMaps;
-
-  public static final class EnumValueInfo {
-    /** The anonymous subtype of this specific value or the enum type. */
-    public final DexType type;
-    public final int ordinal;
-
-    public EnumValueInfo(DexType type, int ordinal) {
-      this.type = type;
-      this.ordinal = ordinal;
-    }
-  }
+  final EnumValueInfoMapCollection enumValueInfoMaps;
 
   final Set<DexType> instantiatedLambdas;
 
@@ -229,7 +219,7 @@
       Object2BooleanMap<DexReference> identifierNameStrings,
       Set<DexType> prunedTypes,
       Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
-      Map<DexType, Map<DexField, EnumValueInfo>> enumValueInfoMaps,
+      EnumValueInfoMapCollection enumValueInfoMaps,
       Set<DexType> instantiatedLambdas,
       Set<DexType> constClassReferences) {
     super(application);
@@ -312,7 +302,7 @@
       Object2BooleanMap<DexReference> identifierNameStrings,
       Set<DexType> prunedTypes,
       Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
-      Map<DexType, Map<DexField, EnumValueInfo>> enumValueInfoMaps,
+      EnumValueInfoMapCollection enumValueInfoMaps,
       Set<DexType> instantiatedLambdas,
       Set<DexType> constClassReferences) {
     super(appInfoWithSubtyping);
@@ -459,7 +449,7 @@
   public AppInfoWithLiveness(
       AppInfoWithLiveness previous,
       Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
-      Map<DexType, Map<DexField, EnumValueInfo>> enumValueInfoMaps) {
+      EnumValueInfoMapCollection enumValueInfoMaps) {
     super(previous);
     this.missingTypes = previous.missingTypes;
     this.liveTypes = previous.liveTypes;
@@ -705,12 +695,12 @@
     return result;
   }
 
-  public Map<DexField, EnumValueInfo> getEnumValueInfoMapFor(DexType enumClass) {
+  public EnumValueInfoMap getEnumValueInfoMap(DexType enumType) {
     assert checkIfObsolete();
-    return enumValueInfoMaps.get(enumClass);
+    return enumValueInfoMaps.getEnumValueInfoMap(enumType);
   }
 
-  public Int2ReferenceMap<DexField> getSwitchMapFor(DexField field) {
+  public Int2ReferenceMap<DexField> getSwitchMap(DexField field) {
     assert checkIfObsolete();
     return switchMaps.get(field);
   }
@@ -1066,7 +1056,7 @@
         // Don't rewrite pruned types - the removed types are identified by their original name.
         prunedTypes,
         rewriteReferenceKeys(switchMaps, lens::lookupField),
-        rewriteEnumValueInfoMaps(enumValueInfoMaps, lens),
+        enumValueInfoMaps.rewrittenWithLens(lens),
         rewriteItems(instantiatedLambdas, lens::lookupType),
         constClassReferences);
   }
@@ -1448,14 +1438,13 @@
     return result == null || !result.isVirtualMethod() ? null : result;
   }
 
-  public AppInfoWithLiveness addSwitchMaps(Map<DexField, Int2ReferenceMap<DexField>> switchMaps) {
+  public AppInfoWithLiveness withSwitchMaps(Map<DexField, Int2ReferenceMap<DexField>> switchMaps) {
     assert checkIfObsolete();
     assert this.switchMaps.isEmpty();
     return new AppInfoWithLiveness(this, switchMaps, enumValueInfoMaps);
   }
 
-  public AppInfoWithLiveness addEnumValueInfoMaps(
-      Map<DexType, Map<DexField, EnumValueInfo>> enumValueInfoMaps) {
+  public AppInfoWithLiveness withEnumValueInfoMaps(EnumValueInfoMapCollection enumValueInfoMaps) {
     assert checkIfObsolete();
     assert this.enumValueInfoMaps.isEmpty();
     return new AppInfoWithLiveness(this, switchMaps, enumValueInfoMaps);
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 309a6e4..36afff6 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -51,6 +51,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.DirectMappedDexApplication.Builder;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
 import com.android.tools.r8.graph.InnerClassAttribute;
@@ -2556,7 +2557,7 @@
             joinIdentifierNameStrings(rootSet.identifierNameStrings, identifierNameStrings),
             Collections.emptySet(),
             Collections.emptyMap(),
-            Collections.emptyMap(),
+            EnumValueInfoMapCollection.empty(),
             SetUtils.mapIdentityHashSet(
                 unknownInstantiatedInterfaceTypes.getItems(), DexProgramClass::getType),
             constClassReferences);