Refactor ConstDynamic usage

- Model better ClassDesc, EnumDesc and
  getStaticFinal library constDynamic

Change-Id: I89fb0e9fb156da978f93a64200aa30d666855a35
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
index b125b83..9ff552c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -187,10 +187,7 @@
   @Override
   void internalRegisterUse(
       UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
-    registry.registerTypeReference(reference.getType());
-    registry.registerMethodHandle(
-        reference.getBootstrapMethod(), NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
-    assert reference.getBootstrapMethodArguments().isEmpty();
+    registry.registerConstDynamic(reference);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 693ff71..02d0092 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -845,21 +845,12 @@
       createStaticallyKnownType("Ljava/lang/invoke/LambdaMetafactory;");
   public final DexType constantBootstrapsType =
       createStaticallyKnownType("Ljava/lang/invoke/ConstantBootstraps;");
+  public final ConstantBootstrapsMembers constantBootstrapsMembers =
+      new ConstantBootstrapsMembers();
   public final DexType switchBootstrapType = createType("Ljava/lang/runtime/SwitchBootstraps;");
   public final DexType callSiteType = createStaticallyKnownType("Ljava/lang/invoke/CallSite;");
   public final DexType lookupType =
       createStaticallyKnownType("Ljava/lang/invoke/MethodHandles$Lookup;");
-  public final DexMethod constantDynamicBootstrapMethod =
-      createMethod(
-          constantBootstrapsType,
-          createProto(
-              objectType,
-              methodHandlesLookupType,
-              stringType,
-              classType,
-              methodHandleType,
-              objectArrayType),
-          invokeMethodName);
   public final DexProto switchBootstrapMethodProto =
       createProto(
           callSiteType, methodHandlesLookupType, stringType, methodTypeType, objectArrayType);
@@ -1890,6 +1881,25 @@
     }
   }
 
+  public class ConstantBootstrapsMembers {
+    public final DexMethod invoke =
+        createMethod(
+            constantBootstrapsType,
+            createProto(
+                objectType,
+                methodHandlesLookupType,
+                stringType,
+                classType,
+                methodHandleType,
+                objectArrayType),
+            invokeMethodName);
+    public final DexMethod getStaticFinal =
+        createMethod(
+            constantBootstrapsType,
+            createProto(objectType, methodHandlesLookupType, stringType, classType),
+            "getStaticFinal");
+  }
+
   public class BufferMembers {
     public final DexMethod positionArg =
         createMethod(bufferType, createProto(bufferType, intType), "position");
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index d138d31..28a12da 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -3,18 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import static com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper.isTypeSwitchCallSite;
+import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.extractClassDescConstantDynamic;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.isClassDescConstantDynamic;
 
 import com.android.tools.r8.dex.code.CfOrDexInstanceFieldRead;
 import com.android.tools.r8.dex.code.CfOrDexInstruction;
 import com.android.tools.r8.dex.code.CfOrDexStaticFieldRead;
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.code.InvokeType;
 import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper;
+import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicReference;
 import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.List;
 import java.util.ListIterator;
 
 public abstract class UseRegistry<T extends Definition> {
@@ -237,67 +239,70 @@
   }
 
   protected void registerCallSiteBootstrapArgs(DexCallSite callSite, int start, int end) {
-    boolean isLambdaMetaFactory =
-        appView.dexItemFactory().isLambdaMetafactoryMethod(callSite.bootstrapMethod.asMethod());
-    // Register bootstrap method arguments.
-    // Only Type, MethodHandle, and MethodType need to be registered.
+    MethodHandleUse use =
+        appView.dexItemFactory().isLambdaMetafactoryMethod(callSite.bootstrapMethod.asMethod())
+            ? MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY
+            : MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+    registerBootstrapArgs(callSite.bootstrapArgs, start, end, use);
+  }
+
+  protected void registerBootstrapArgs(
+      List<DexValue> bootstrapArgs, int start, int end, MethodHandleUse use) {
     assert start >= 0;
-    assert end <= callSite.bootstrapArgs.size();
+    assert end <= bootstrapArgs.size();
     for (int i = start; i < end; i++) {
-      DexValue arg = callSite.bootstrapArgs.get(i);
-      switch (arg.getValueKind()) {
-        case METHOD_HANDLE:
-          DexMethodHandle handle = arg.asDexValueMethodHandle().value;
-          MethodHandleUse use =
-              isLambdaMetaFactory
-                  ? MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY
-                  : MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
-          registerMethodHandle(handle, use);
-          break;
-        case METHOD_TYPE:
-          registerProto(arg.asDexValueMethodType().value);
-          break;
-        case TYPE:
-          registerTypeReference(arg.asDexValueType().value);
-          break;
-        case CONST_DYNAMIC:
-          if (!isTypeSwitchCallSite(callSite, appView.dexItemFactory())) {
-            throw new CompilationError(
-                "Unsupported const dynamic in call site " + arg, getContext().getOrigin());
-          }
-          if (arg.asDexValueConstDynamic()
-              .getValue()
-              .getType()
-              .isIdenticalTo(appView.dexItemFactory().enumDescType)) {
-            TypeSwitchDesugaringHelper.dispatchEnumField(
-                (type, fieldName) -> {
-                  registerTypeReference(type);
-                },
-                arg.asDexValueConstDynamic().getValue(),
-                context,
-                dexItemFactory());
-          }
-          break;
-        default:
-          assert arg.isDexValueInt()
-              || arg.isDexValueLong()
-              || arg.isDexValueFloat()
-              || arg.isDexValueDouble()
-              || arg.isDexValueString()
-              || arg.isDexValueResourceNumber();
-          break;
-      }
+      DexValue arg = bootstrapArgs.get(i);
+      registerBoostrapArg(arg, use);
       if (continuation.shouldBreak()) {
         break;
       }
     }
   }
 
+  protected void registerBoostrapArg(DexValue arg, MethodHandleUse use) {
+    switch (arg.getValueKind()) {
+      case METHOD_HANDLE:
+        DexMethodHandle handle = arg.asDexValueMethodHandle().value;
+        registerMethodHandle(handle, use);
+        break;
+      case METHOD_TYPE:
+        registerProto(arg.asDexValueMethodType().value);
+        break;
+      case TYPE:
+        registerTypeReference(arg.asDexValueType().value);
+        break;
+      case CONST_DYNAMIC:
+        ConstantDynamicReference constDynamic = arg.asDexValueConstDynamic().getValue();
+        if (isClassDescConstantDynamic(constDynamic, dexItemFactory())) {
+          registerTypeReference(
+              extractClassDescConstantDynamic(constDynamic, dexItemFactory(), context));
+        } else {
+          registerConstDynamic(constDynamic);
+        }
+        break;
+      default:
+        assert arg.isDexValueInt()
+            || arg.isDexValueLong()
+            || arg.isDexValueFloat()
+            || arg.isDexValueDouble()
+            || arg.isDexValueString()
+            || arg.isDexValueResourceNumber();
+        break;
+    }
+  }
+
   public void registerCallSite(DexCallSite callSite) {
     registerCallSiteExceptBootstrapArgs(callSite);
     registerCallSiteBootstrapArgs(callSite, 0, callSite.bootstrapArgs.size());
   }
 
+  public void registerConstDynamic(ConstantDynamicReference reference) {
+    registerTypeReference(reference.getType());
+    registerMethodHandle(reference.getBootstrapMethod(), NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+    List<DexValue> bootArgs = reference.getBootstrapMethodArguments();
+    registerBootstrapArgs(bootArgs, 0, bootArgs.size(), NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+  }
+
   public void registerProto(DexProto proto) {
     registerTypeReference(proto.returnType);
     for (DexType type : proto.parameters.values) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/LibraryConstantDynamic.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/LibraryConstantDynamic.java
new file mode 100644
index 0000000..5c36c18
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/LibraryConstantDynamic.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2025, 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.ir.desugar.constantdynamic;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.Definition;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.function.BiConsumer;
+
+public class LibraryConstantDynamic {
+
+  private static CompilationError throwInvalidLibraryConstantDynamic(
+      String msg, Definition context) {
+    throw new CompilationError("Invalid library ConstantDynamic: " + msg, context.getOrigin());
+  }
+
+  private static boolean methodHandleIsInvokeStaticTo(DexValue dexValue, DexMethod method) {
+    if (!dexValue.isDexValueMethodHandle()) {
+      return false;
+    }
+    DexMethodHandle methodHandle = dexValue.asDexValueMethodHandle().getValue();
+    return methodHandle.type.isInvokeStatic() && methodHandle.asMethod().isIdenticalTo(method);
+  }
+
+  private static boolean isInvokeLibraryConstantDynamic(
+      ConstantDynamicReference constantDynamic,
+      DexType type,
+      int argSize,
+      DexMethod methodHandleMethod,
+      DexItemFactory factory) {
+    DexMethod bootstrapMethod = factory.constantBootstrapsMembers.invoke;
+    return constantDynamic.getType().isIdenticalTo(type)
+        && constantDynamic.getName().isIdenticalTo(bootstrapMethod.getName())
+        && constantDynamic.getBootstrapMethod().asMethod().isIdenticalTo(bootstrapMethod)
+        && constantDynamic.getBootstrapMethodArguments().size() == argSize
+        && methodHandleIsInvokeStaticTo(
+            constantDynamic.getBootstrapMethodArguments().get(0), methodHandleMethod);
+  }
+
+  public static boolean isBoxedBooleanConstantDynamic(
+      ConstantDynamicReference constantDynamic, DexItemFactory factory) {
+    DexMethod bootstrapMethod = factory.constantBootstrapsMembers.getStaticFinal;
+    return constantDynamic.getType().isIdenticalTo(factory.boxedBooleanType)
+        && constantDynamic.getBootstrapMethod().asMethod().isIdenticalTo(bootstrapMethod)
+        && constantDynamic.getBootstrapMethodArguments().size() == 0;
+  }
+
+  public static boolean extractBoxedBooleanConstantDynamic(
+      ConstantDynamicReference constantDynamic, DexItemFactory factory, Definition context) {
+    assert isBoxedBooleanConstantDynamic(constantDynamic, factory);
+    String name = constantDynamic.getName().toString();
+    if (name.equals("TRUE")) {
+      return true;
+    }
+    if (name.equals("FALSE")) {
+      return false;
+    }
+    throw throwInvalidLibraryConstantDynamic("Invalid Boolean arg " + name, context);
+  }
+
+  public static boolean isClassDescConstantDynamic(
+      ConstantDynamicReference constantDynamic, DexItemFactory factory) {
+    return isInvokeLibraryConstantDynamic(
+        constantDynamic, factory.classDescType, 2, factory.classDescMethod, factory);
+  }
+
+  public static DexType extractClassDescConstantDynamic(
+      ConstantDynamicReference constantDynamic, DexItemFactory factory, Definition context) {
+    assert isClassDescConstantDynamic(constantDynamic, factory);
+    DexValue dexValueClassName = constantDynamic.getBootstrapMethodArguments().get(1);
+    if (!dexValueClassName.isDexValueString()) {
+      throw throwInvalidLibraryConstantDynamic("Class name " + dexValueClassName, context);
+    }
+    DexString className = dexValueClassName.asDexValueString().getValue();
+    return factory.createType(DescriptorUtils.javaTypeToDescriptor(className.toString()));
+  }
+
+  public static boolean isEnumDescConstantDynamic(
+      ConstantDynamicReference constantDynamic, DexItemFactory factory) {
+    return isInvokeLibraryConstantDynamic(
+        constantDynamic, factory.enumDescType, 3, factory.enumDescMethod, factory);
+  }
+
+  public static void dispatchEnumDescConstantDynamic(
+      ConstantDynamicReference constantDynamic,
+      DexItemFactory factory,
+      Definition context,
+      BiConsumer<DexType, DexString> consumer) {
+    assert isEnumDescConstantDynamic(constantDynamic, factory);
+    DexValue dexValueFieldName = constantDynamic.getBootstrapMethodArguments().get(2);
+    if (!dexValueFieldName.isDexValueString()) {
+      throw throwInvalidLibraryConstantDynamic("Enum field name " + dexValueFieldName, context);
+    }
+    DexValue dexValueClassCstDynamic = constantDynamic.getBootstrapMethodArguments().get(1);
+    if (!dexValueClassCstDynamic.isDexValueConstDynamic()) {
+      throw throwInvalidLibraryConstantDynamic("Enum class " + dexValueClassCstDynamic, context);
+    }
+    ConstantDynamicReference classCstDynamic =
+        dexValueClassCstDynamic.asDexValueConstDynamic().getValue();
+    if (!isClassDescConstantDynamic(classCstDynamic, factory)) {
+      throw throwInvalidLibraryConstantDynamic("Enum class " + dexValueClassCstDynamic, context);
+    }
+    consumer.accept(
+        extractClassDescConstantDynamic(classCstDynamic, factory, context),
+        dexValueFieldName.asDexValueString().getValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaring.java
index 898f0f9..c0b0146 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaring.java
@@ -4,10 +4,12 @@
 
 package com.android.tools.r8.ir.desugar.typeswitch;
 
-import static com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper.dispatchEnumField;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.dispatchEnumDescConstantDynamic;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.extractBoxedBooleanConstantDynamic;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.isBoxedBooleanConstantDynamic;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.isEnumDescConstantDynamic;
 import static com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper.isEnumSwitchCallSite;
 import static com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper.isTypeSwitchCallSite;
-import static com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper.methodHandleIsInvokeStaticTo;
 
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
@@ -20,7 +22,6 @@
 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.DexValue;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
@@ -30,7 +31,6 @@
 import com.android.tools.r8.ir.synthetic.TypeSwitchSyntheticCfCodeProvider.Dispatcher;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 import org.objectweb.asm.Opcodes;
 
@@ -222,11 +222,12 @@
         dexStringConsumer.accept(dexValue.asDexValueString().getValue());
       } else if (dexValue.isDexValueConstDynamic()) {
         ConstantDynamicReference constDynamic = dexValue.asDexValueConstDynamic().getValue();
-        if (constDynamic.getType().isIdenticalTo(factory.boxedBooleanType)) {
-          dispatchBooleanField(context, dexValue, booleanConsumer, constDynamic);
+        if (isBoxedBooleanConstantDynamic(constDynamic, factory)) {
+          booleanConsumer.accept(
+              extractBoxedBooleanConstantDynamic(constDynamic, factory, context));
         } else {
-          assert constDynamic.getType().isIdenticalTo(factory.enumDescType);
-          dispatchEnumField(enumConsumer, constDynamic, context, appView.dexItemFactory());
+          assert isEnumDescConstantDynamic(constDynamic, factory);
+          dispatchEnumDescConstantDynamic(constDynamic, factory, context, enumConsumer);
         }
       } else if (dexValue.isDexValueNumber()) {
         assert dexValue.isDexValueDouble()
@@ -240,35 +241,6 @@
     };
   }
 
-  private void dispatchBooleanField(
-      ProgramMethod context,
-      DexValue dexValue,
-      Consumer<Boolean> booleanConsumer,
-      ConstantDynamicReference constDynamic) {
-    if (methodHandleIsInvokeStaticTo(
-        constDynamic.getBootstrapMethod(),
-        factory.createMethod(
-            factory.constantBootstrapsType,
-            factory.createProto(
-                factory.objectType,
-                factory.methodHandlesLookupType,
-                factory.stringType,
-                factory.classType),
-            "getStaticFinal"))) {
-      String name = constDynamic.getName().toString();
-      if (name.equals("TRUE")) {
-        booleanConsumer.accept(true);
-        return;
-      }
-      if (name.equals("FALSE")) {
-        booleanConsumer.accept(false);
-        return;
-      }
-    }
-    throw new CompilationError(
-        "Invalid Boolean bootstrap arg for type switch " + dexValue, context.getOrigin());
-  }
-
   private Scanner typeScanner() {
     return (dexValue, intEqCheck, enumCase) -> {
       if (dexValue.isDexValueInt()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaringHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaringHelper.java
index 2780c66..d38915a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaringHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/typeswitch/TypeSwitchDesugaringHelper.java
@@ -4,25 +4,14 @@
 
 package com.android.tools.r8.ir.desugar.typeswitch;
 
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicReference;
-import com.android.tools.r8.utils.DescriptorUtils;
-import java.util.function.BiConsumer;
 
 public class TypeSwitchDesugaringHelper {
-  private static CompilationError throwEnumFieldConstantDynamic(String msg, Definition context) {
-    throw new CompilationError(
-        "Unexpected ConstantDynamic in TypeSwitch: " + msg, context.getOrigin());
-  }
 
   private static boolean isTypeSwitchProto(DexProto proto) {
     return proto.getReturnType().isIntType()
@@ -42,14 +31,7 @@
         && methodHandleIsInvokeStaticTo(callSite.bootstrapMethod, factory.enumSwitchMethod);
   }
 
-  private static boolean methodHandleIsInvokeStaticTo(DexValue dexValue, DexMethod method) {
-    if (!dexValue.isDexValueMethodHandle()) {
-      return false;
-    }
-    return methodHandleIsInvokeStaticTo(dexValue.asDexValueMethodHandle().getValue(), method);
-  }
-
-  public static boolean methodHandleIsInvokeStaticTo(
+  private static boolean methodHandleIsInvokeStaticTo(
       DexMethodHandle methodHandle, DexMethod method) {
     return methodHandle.type.isInvokeStatic() && methodHandle.asMethod().isIdenticalTo(method);
   }
@@ -59,47 +41,4 @@
         && methodProto.getArity() == 2
         && methodProto.getParameter(1).isIdenticalTo(intType);
   }
-
-  public static void dispatchEnumField(
-      BiConsumer<DexType, DexString> enumConsumer,
-      ConstantDynamicReference enumCstDynamic,
-      Definition context,
-      DexItemFactory factory) {
-    DexMethod bootstrapMethod = factory.constantDynamicBootstrapMethod;
-    if (!(enumCstDynamic.getType().isIdenticalTo(factory.enumDescType)
-        && enumCstDynamic.getName().isIdenticalTo(bootstrapMethod.getName())
-        && enumCstDynamic.getBootstrapMethod().asMethod().isIdenticalTo(bootstrapMethod)
-        && enumCstDynamic.getBootstrapMethodArguments().size() == 3
-        && methodHandleIsInvokeStaticTo(
-            enumCstDynamic.getBootstrapMethodArguments().get(0), factory.enumDescMethod))) {
-      throw throwEnumFieldConstantDynamic("Invalid EnumDesc", context);
-    }
-    DexValue dexValueFieldName = enumCstDynamic.getBootstrapMethodArguments().get(2);
-    if (!dexValueFieldName.isDexValueString()) {
-      throw throwEnumFieldConstantDynamic("Field name " + dexValueFieldName, context);
-    }
-    DexString fieldName = dexValueFieldName.asDexValueString().getValue();
-
-    DexValue dexValueClassCstDynamic = enumCstDynamic.getBootstrapMethodArguments().get(1);
-    if (!dexValueClassCstDynamic.isDexValueConstDynamic()) {
-      throw throwEnumFieldConstantDynamic("Enum class " + dexValueClassCstDynamic, context);
-    }
-    ConstantDynamicReference classCstDynamic =
-        dexValueClassCstDynamic.asDexValueConstDynamic().getValue();
-    if (!(classCstDynamic.getType().isIdenticalTo(factory.classDescType)
-        && classCstDynamic.getName().isIdenticalTo(bootstrapMethod.getName())
-        && classCstDynamic.getBootstrapMethod().asMethod().isIdenticalTo(bootstrapMethod)
-        && classCstDynamic.getBootstrapMethodArguments().size() == 2
-        && methodHandleIsInvokeStaticTo(
-            classCstDynamic.getBootstrapMethodArguments().get(0), factory.classDescMethod))) {
-      throw throwEnumFieldConstantDynamic("Class descriptor " + classCstDynamic, context);
-    }
-    DexValue dexValueClassName = classCstDynamic.getBootstrapMethodArguments().get(1);
-    if (!dexValueClassName.isDexValueString()) {
-      throw throwEnumFieldConstantDynamic("Class name " + dexValueClassName, context);
-    }
-    DexString className = dexValueClassName.asDexValueString().getValue();
-    DexType type = factory.createType(DescriptorUtils.javaTypeToDescriptor(className.toString()));
-    enumConsumer.accept(type, fieldName);
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 1e92c88..d328deb 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.dispatchEnumDescConstantDynamic;
+import static com.android.tools.r8.ir.desugar.constantdynamic.LibraryConstantDynamic.isEnumDescConstantDynamic;
 import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
 import static com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper.isEnumSwitchCallSite;
 import static com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper.isTypeSwitchCallSite;
@@ -28,7 +31,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.InvokeType;
 import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaringHelper;
 import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
 import java.util.ListIterator;
@@ -309,21 +311,15 @@
 
   private void registerTypeSwitchCallSiteBootstrapArgs(DexCallSite callSite) {
     for (DexValue bootstrapArg : callSite.bootstrapArgs) {
-      if (bootstrapArg.isDexValueType()) {
-        registerTypeReference(bootstrapArg.asDexValueType().value);
-      } else if (bootstrapArg.isDexValueConstDynamic()
-          && bootstrapArg
-              .asDexValueConstDynamic()
-              .getValue()
-              .getType()
-              .isIdenticalTo(appView.dexItemFactory().enumDescType)) {
-        TypeSwitchDesugaringHelper.dispatchEnumField(
-            (type, fieldName) -> {
-              registerEnumReferencedInTypeSwitchBootstrapArguments(type);
-            },
+      registerBoostrapArg(bootstrapArg, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+      if (bootstrapArg.isDexValueConstDynamic()
+          && isEnumDescConstantDynamic(
+              bootstrapArg.asDexValueConstDynamic().getValue(), dexItemFactory())) {
+        dispatchEnumDescConstantDynamic(
             bootstrapArg.asDexValueConstDynamic().getValue(),
+            dexItemFactory(),
             getContext(),
-            dexItemFactory());
+            (type, name) -> registerEnumReferencedInTypeSwitchBootstrapArguments(type));
       }
     }
   }
@@ -331,9 +327,8 @@
   private void registerEnumSwitchCallSiteBootstrapArgs(DexCallSite callSite) {
     DexType enumType = callSite.getMethodProto().getParameter(0);
     for (DexValue bootstrapArg : callSite.bootstrapArgs) {
-      if (bootstrapArg.isDexValueType()) {
-        registerTypeReference(bootstrapArg.asDexValueType().value);
-      } else if (bootstrapArg.isDexValueString()) {
+      registerBoostrapArg(bootstrapArg, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+      if (bootstrapArg.isDexValueString()) {
         registerEnumReferencedInTypeSwitchBootstrapArguments(enumType);
       }
     }
@@ -344,7 +339,7 @@
     // The Instance Get method handle in invokeDynamicOnRecord are considered:
     // - a record use if not a constant value,
     // - unused if a constant value.
-    registerCallSiteBootstrapArgs(callSite, 0, 2);
+    registerBootstrapArgs(callSite.getBootstrapArgs(), 0, 2, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
     for (int i = 2; i < callSite.getBootstrapArgs().size(); i++) {
       DexField field = callSite.getBootstrapArgs().get(i).asDexValueMethodHandle().value.asField();
       DexEncodedField encodedField =