Revert "Extend and disable-by-default Enum reflection tracing"

This reverts commit cb8bff39f56cea0196ab1163d7ffe08334104b85.

It's possible that some people rely on Enum.valueOf() reflection, since
support for that has existed for some time.

Bug: b/204939965
Change-Id: Iffb606cc6224eb9ab857bf31741cd03d650257a8
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 1aa0120..27f31fa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -640,6 +640,9 @@
       createStaticallyKnownType("Ljava/util/logging/Level;");
   public final DexType javaUtilLoggingLoggerType =
       createStaticallyKnownType("Ljava/util/logging/Logger;");
+  public final DexType javaUtilEnumMapType = createStaticallyKnownType("Ljava/util/EnumMap;");
+  public final DexType javaUtilEnumSetType = createStaticallyKnownType("Ljava/util/EnumSet;");
+
   public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
   public final DexType androidAppFragment = createStaticallyKnownType("Landroid/app/Fragment;");
   public final DexType androidAppZygotePreload =
@@ -761,6 +764,8 @@
   public final JavaUtilLocaleMembers javaUtilLocaleMembers = new JavaUtilLocaleMembers();
   public final JavaUtilLoggingLevelMembers javaUtilLoggingLevelMembers =
       new JavaUtilLoggingLevelMembers();
+  public final JavaUtilEnumMapMembers javaUtilEnumMapMembers = new JavaUtilEnumMapMembers();
+  public final JavaUtilEnumSetMembers javaUtilEnumSetMembers = new JavaUtilEnumSetMembers();
 
   public final List<LibraryMembers> libraryMembersCollection =
       ImmutableList.of(
@@ -1609,6 +1614,28 @@
     }
   }
 
+  public class JavaUtilEnumMapMembers {
+    public final DexMethod constructor =
+        createMethod(javaUtilEnumMapType, createProto(voidType, classType), constructorMethodName);
+  }
+
+  public class JavaUtilEnumSetMembers {
+    private final DexString allOfString = createString("allOf");
+    private final DexString noneOfString = createString("noneOf");
+    private final DexString rangeString = createString("range");
+
+    public boolean isFactoryMethod(DexMethod invokedMethod) {
+      if (!invokedMethod.getHolderType().equals(javaUtilEnumSetType)) {
+        return false;
+      }
+      DexString name = invokedMethod.getName();
+      return name.isIdenticalTo(allOfString)
+          || name.isIdenticalTo(noneOfString)
+          || name.isIdenticalTo(ofMethodName)
+          || name.isIdenticalTo(rangeString);
+    }
+  }
+
   public class LongMembers extends BoxedPrimitiveMembers {
 
     public final DexField TYPE = createField(boxedLongType, classType, "TYPE");
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 d179979..fdc7fb5 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -99,7 +99,9 @@
 import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
 import com.android.tools.r8.ir.analysis.proto.ProtoEnqueuerUseRegistry;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoEnqueuerExtension;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstantValueUtils;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -551,10 +553,6 @@
       ProtoEnqueuerExtension.register(appView, analysesBuilder);
       ResourceAccessAnalysis.register(appView, this, analysesBuilder);
       RuntimeTypeCheckInfo.register(runtimeTypeCheckInfoBuilder, analysesBuilder);
-      if (options.experimentalTraceEnumReflection) {
-        EnqueuerEnumReflectionSupport.register(
-            appView, this::markEnumValuesAsReachable, analysesBuilder);
-      }
     }
     analyses = analysesBuilder.build();
 
@@ -1550,6 +1548,11 @@
     MethodResolutionResult resolutionResult =
         handleInvokeOfDirectTarget(invokedMethod, context, reason);
     analyses.traceInvokeDirect(invokedMethod, resolutionResult, context);
+
+    if (invokedMethod.equals(appView.dexItemFactory().javaUtilEnumMapMembers.constructor)) {
+      // EnumMap uses reflection.
+      pendingReflectiveUses.add(context);
+    }
   }
 
   void traceInvokeInterface(
@@ -1604,6 +1607,10 @@
       identifierNameStrings.add(invokedMethod);
       // Revisit the current method to implicitly add -keep rule for items with reflective access.
       pendingReflectiveUses.add(context);
+    } else if (invokedMethod == dexItemFactory.enumMembers.valueOf
+        || dexItemFactory.javaUtilEnumSetMembers.isFactoryMethod(invokedMethod)) {
+      // See comment in handleEnumValueOfOrCollectionInstantiation.
+      pendingReflectiveUses.add(context);
     } else if (invokedMethod == dexItemFactory.proxyMethods.newProxyInstance) {
       pendingReflectiveUses.add(context);
     } else if (dexItemFactory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
@@ -1640,6 +1647,7 @@
         invokedMethod, context, registry, KeepReason.invokedFromLambdaCreatedIn(context));
   }
 
+  @SuppressWarnings("ReferenceEquality")
   private void traceInvokeVirtual(
       DexMethod invokedMethod,
       ProgramMethod context,
@@ -1648,11 +1656,10 @@
     if (registry != null && !registry.markInvokeVirtualAsSeen(invokedMethod)) {
       return;
     }
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    if (invokedMethod.isIdenticalTo(dexItemFactory.classMethods.newInstance)
-        || invokedMethod.isIdenticalTo(dexItemFactory.constructorMethods.newInstance)) {
+    if (invokedMethod == appView.dexItemFactory().classMethods.newInstance
+        || invokedMethod == appView.dexItemFactory().constructorMethods.newInstance) {
       pendingReflectiveUses.add(context);
-    } else if (dexItemFactory.classMethods.isReflectiveMemberLookup(invokedMethod)) {
+    } else if (appView.dexItemFactory().classMethods.isReflectiveMemberLookup(invokedMethod)) {
       // Implicitly add -identifiernamestring rule for the Java reflection in use.
       identifierNameStrings.add(invokedMethod);
       // Revisit the current method to implicitly add -keep rule for items with reflective access.
@@ -5217,24 +5224,33 @@
     InstructionIterator iterator = code.instructionIterator();
     while (iterator.hasNext()) {
       Instruction instruction = iterator.next();
-      if (instruction.isInvokeMethod()) {
-        handleReflectiveBehavior(method, instruction.asInvokeMethod());
-      }
+      handleReflectiveBehavior(method, instruction);
     }
   }
 
-  private void handleReflectiveBehavior(ProgramMethod method, InvokeMethod invoke) {
+  @SuppressWarnings("ReferenceEquality")
+  private void handleReflectiveBehavior(ProgramMethod method, Instruction instruction) {
+    if (!instruction.isInvokeMethod()) {
+      return;
+    }
+    InvokeMethod invoke = instruction.asInvokeMethod();
     DexMethod invokedMethod = invoke.getInvokedMethod();
     DexItemFactory dexItemFactory = appView.dexItemFactory();
-    if (invokedMethod.isIdenticalTo(dexItemFactory.classMethods.newInstance)) {
+    if (invokedMethod == dexItemFactory.classMethods.newInstance) {
       handleJavaLangClassNewInstance(method, invoke);
       return;
     }
-    if (invokedMethod.isIdenticalTo(dexItemFactory.constructorMethods.newInstance)) {
+    if (invokedMethod == dexItemFactory.constructorMethods.newInstance) {
       handleJavaLangReflectConstructorNewInstance(method, invoke);
       return;
     }
-    if (invokedMethod.isIdenticalTo(dexItemFactory.proxyMethods.newProxyInstance)) {
+    if (invokedMethod == dexItemFactory.enumMembers.valueOf
+        || invokedMethod == dexItemFactory.javaUtilEnumMapMembers.constructor
+        || dexItemFactory.javaUtilEnumSetMembers.isFactoryMethod(invokedMethod)) {
+      handleEnumValueOfOrCollectionInstantiation(method, invoke);
+      return;
+    }
+    if (invokedMethod == dexItemFactory.proxyMethods.newProxyInstance) {
       handleJavaLangReflectProxyNewProxyInstance(method, invoke);
       return;
     }
@@ -5557,6 +5573,47 @@
     }
   }
 
+  private void handleEnumValueOfOrCollectionInstantiation(
+      ProgramMethod context, InvokeMethod invoke) {
+    if (invoke.inValues().isEmpty()) {
+      // Should never happen.
+      return;
+    }
+
+    // The use of java.lang.Enum.valueOf(java.lang.Class, java.lang.String) will indirectly
+    // access the values() method of the enum class passed as the first argument. The method
+    // SomeEnumClass.valueOf(java.lang.String) which is generated by javac for all enums will
+    // call this method.
+    // Likewise, EnumSet and EnumMap call values() on the passed in Class.
+    Value firstArg = invoke.getFirstNonReceiverArgument();
+    if (firstArg.isPhi()) {
+      return;
+    }
+    DexType type;
+    if (invoke
+        .getInvokedMethod()
+        .getParameter(0)
+        .isIdenticalTo(appView.dexItemFactory().classType)) {
+      // EnumMap.<init>(), EnumSet.noneOf(), EnumSet.allOf(), Enum.valueOf().
+      ConstClass constClass = firstArg.definition.asConstClass();
+      if (constClass == null || !constClass.getType().isClassType()) {
+        return;
+      }
+      type = constClass.getType();
+    } else {
+      // EnumSet.of(), EnumSet.range()
+      ClassTypeElement typeElement = firstArg.getType().asClassType();
+      if (typeElement == null) {
+        return;
+      }
+      type = typeElement.getClassType();
+    }
+    DexProgramClass clazz = getProgramClassOrNull(type, context);
+    if (clazz != null && clazz.isEnum()) {
+      markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(context));
+    }
+  }
+
   private void handleServiceLoaderInvocation(ProgramMethod method, InvokeMethod invoke) {
     if (invoke.inValues().isEmpty()) {
       // Should never happen.
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerEnumReflectionSupport.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerEnumReflectionSupport.java
deleted file mode 100644
index 376a723..0000000
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerEnumReflectionSupport.java
+++ /dev/null
@@ -1,336 +0,0 @@
-// 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.shaking;
-
-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.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-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.MethodResolutionResult;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.analysis.EnqueuerAnalysisCollection;
-import com.android.tools.r8.graph.analysis.FixpointEnqueuerAnalysis;
-import com.android.tools.r8.graph.analysis.TraceInvokeEnqueuerAnalysis;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstClass;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions;
-import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
-
-/**
- * Finds Enum.valueOf() methods that must be kept due to uses of OS APIs that use it via reflection.
- *
- * <p>Looks for:
- *
- * <ul>
- *   <li>Enum.valueOf()
- *   <li>EnumSet.allOf()
- *   <li>EnumSet.noneOf()
- *   <li>EnumSet.range()
- *   <li>new EnumMap()
- *   <li>Parcel.readSerializable()
- *   <li>Bundle.getSerializable()
- *   <li>Intent.getSerializableExtra()
- *   <li>ObjectInputStream.readObject()
- * </ul>
- *
- * <p>Similar to non-enum reflection calls, tracing is best-effort. E.g.:
- *
- * <ul>
- *   <li>For the overloads that accept a Class, it must be a const-class
- *   <li>For others, the return value must be directly used by a checked-cast of the Enum type.
- * </ul>
- */
-public class EnqueuerEnumReflectionSupport
-    implements TraceInvokeEnqueuerAnalysis, FixpointEnqueuerAnalysis {
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final BiConsumer<DexProgramClass, KeepReason> markAsReachableCallback;
-
-  private final DexString enumSetAllOfString;
-  private final DexString enumSetNoneOfString;
-  private final DexString enumSetRangeString;
-  private final DexString enumSetOfString;
-  private final DexType androidContentIntentType;
-  private final DexType androidOsParcelType;
-  private final DexType javaIoObjectInputStreamType;
-  private final DexType javaUtilEnumMapType;
-  private final DexType javaUtilEnumSetType;
-
-  private final DexMethod intentGetSerializableExtra1;
-  private final DexMethod intentGetSerializableExtra2;
-  private final DexMethod bundleGetSerializable1;
-  private final DexMethod bundleGetSerializable2;
-  private final DexMethod parcelReadSerializable1;
-  private final DexMethod parcelReadSerializable2;
-
-  private final DexMethod objectInputStreamReadObject;
-  private final DexMethod enumValueOfMethod;
-  private final DexMethod enumMapConstructor;
-  private final ProgramMethodSet pendingReflectiveUses = ProgramMethodSet.createLinked();
-
-  public EnqueuerEnumReflectionSupport(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
-      BiConsumer<DexProgramClass, KeepReason> markAsReachableCallback) {
-    this.appView = appView;
-    this.markAsReachableCallback = markAsReachableCallback;
-
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-
-    enumSetAllOfString = dexItemFactory.createString("allOf");
-    enumSetNoneOfString = dexItemFactory.createString("noneOf");
-    enumSetRangeString = dexItemFactory.createString("range");
-    enumSetOfString = dexItemFactory.ofMethodName;
-
-    androidContentIntentType = dexItemFactory.createType("Landroid/content/Intent;");
-    androidOsParcelType = dexItemFactory.createType("Landroid/os/Parcel;");
-    javaIoObjectInputStreamType = dexItemFactory.createType("Ljava/io/ObjectInputStream;");
-    javaUtilEnumMapType = dexItemFactory.createType("Ljava/util/EnumMap;");
-    javaUtilEnumSetType = dexItemFactory.createType("Ljava/util/EnumSet;");
-
-    intentGetSerializableExtra1 =
-        dexItemFactory.createMethod(
-            androidContentIntentType,
-            dexItemFactory.createProto(dexItemFactory.serializableType, dexItemFactory.stringType),
-            "getSerializableExtra");
-    intentGetSerializableExtra2 =
-        dexItemFactory.createMethod(
-            androidContentIntentType,
-            dexItemFactory.createProto(
-                dexItemFactory.serializableType,
-                dexItemFactory.stringType,
-                dexItemFactory.classType),
-            "getSerializableExtra");
-    bundleGetSerializable1 =
-        dexItemFactory.createMethod(
-            dexItemFactory.androidOsBundleType,
-            dexItemFactory.createProto(dexItemFactory.serializableType, dexItemFactory.stringType),
-            "getSerializable");
-    bundleGetSerializable2 =
-        dexItemFactory.createMethod(
-            dexItemFactory.androidOsBundleType,
-            dexItemFactory.createProto(
-                dexItemFactory.serializableType,
-                dexItemFactory.stringType,
-                dexItemFactory.classType),
-            "getSerializable");
-    parcelReadSerializable1 =
-        dexItemFactory.createMethod(
-            androidOsParcelType,
-            dexItemFactory.createProto(dexItemFactory.serializableType),
-            "readSerializable");
-    parcelReadSerializable2 =
-        dexItemFactory.createMethod(
-            androidOsParcelType,
-            dexItemFactory.createProto(
-                dexItemFactory.serializableType,
-                dexItemFactory.classLoaderType,
-                dexItemFactory.classType),
-            "readSerializable");
-    objectInputStreamReadObject =
-        dexItemFactory.createMethod(
-            javaIoObjectInputStreamType,
-            dexItemFactory.createProto(dexItemFactory.objectType),
-            "readObject");
-    enumValueOfMethod = dexItemFactory.enumMembers.valueOf;
-    enumMapConstructor =
-        dexItemFactory.createMethod(
-            javaUtilEnumMapType,
-            dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.classType),
-            dexItemFactory.constructorMethodName);
-  }
-
-  public static void register(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
-      BiConsumer<DexProgramClass, KeepReason> markAsReachableCallback,
-      EnqueuerAnalysisCollection.Builder builder) {
-    EnqueuerEnumReflectionSupport instance =
-        new EnqueuerEnumReflectionSupport(appView, markAsReachableCallback);
-    builder.addTraceInvokeAnalysis(instance);
-    builder.addFixpointAnalysis(instance);
-  }
-
-  private DexProgramClass maybeGetProgramEnumType(DexType type, boolean checkSuper) {
-    // Arrays can be used for serialization-related methods.
-    if (type.isArrayType()) {
-      type = type.toBaseType(appView.dexItemFactory());
-      if (!type.isClassType()) {
-        return null;
-      }
-    }
-
-    DexClass dexClass = appView.definitionFor(type);
-    if (dexClass == null || !dexClass.isProgramClass() || !dexClass.hasSuperType()) {
-      return null;
-    }
-    DexType superType = dexClass.getSuperType();
-    if (superType.isIdenticalTo(appView.dexItemFactory().enumType)) {
-      return dexClass.asProgramClass();
-    }
-    // Cannot have a sub-sub-type of an Enum.
-    return checkSuper ? maybeGetProgramEnumType(superType, false) : null;
-  }
-
-  private void handleEnumCastFromSerializable(ProgramMethod context, Value value) {
-    if (value == null || value.isPhi()) {
-      return;
-    }
-    for (Instruction user : value.aliasedUsers()) {
-      CheckCast castInstr = user.asCheckCast();
-      if (castInstr == null) {
-        continue;
-      }
-      DexProgramClass enumClass = maybeGetProgramEnumType(castInstr.getType(), true);
-      if (enumClass != null) {
-        markAsReachableCallback.accept(enumClass, KeepReason.invokedFrom(context));
-      }
-    }
-  }
-
-  private void handleReflectiveEnumInvoke(
-      ProgramMethod context, InvokeMethod invoke, int classParamIndex) {
-    if (invoke.inValues().size() <= classParamIndex) {
-      // Should never happen.
-      return;
-    }
-
-    // The use of java.lang.Enum.valueOf(java.lang.Class, java.lang.String) will indirectly
-    // access the values() method of the enum class passed as the first argument. The method
-    // SomeEnumClass.valueOf(java.lang.String) which is generated by javac for all enums will
-    // call this method.
-    // Likewise, EnumSet and EnumMap call values() on the passed in Class.
-    Value classArg = invoke.getArgumentForParameter(classParamIndex);
-    if (classArg.isPhi()) {
-      return;
-    }
-    DexType type;
-    if (invoke
-        .getInvokedMethod()
-        .getParameter(classParamIndex)
-        .isIdenticalTo(appView.dexItemFactory().classType)) {
-      // EnumMap.<init>(), EnumSet.noneOf(), EnumSet.allOf(), Enum.valueOf().
-      ConstClass constClass = classArg.definition.asConstClass();
-      if (constClass == null) {
-        return;
-      }
-      type = constClass.getType();
-    } else {
-      // EnumSet.of(), EnumSet.range()
-      ClassTypeElement typeElement = classArg.getType().asClassType();
-      if (typeElement == null) {
-        return;
-      }
-      type = typeElement.getClassType();
-    }
-    // Arrays can be used for serialization-related methods.
-    if (type.isArrayType()) {
-      type = type.toBaseType(appView.dexItemFactory());
-    }
-    DexClass clazz = appView.definitionFor(type, context);
-    if (clazz != null && clazz.isProgramClass() && clazz.isEnum()) {
-      markAsReachableCallback.accept(clazz.asProgramClass(), KeepReason.invokedFrom(context));
-    }
-  }
-
-  private void handleReflectiveBehavior(ProgramMethod method) {
-    IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
-    InstructionIterator iterator = code.instructionIterator();
-    while (iterator.hasNext()) {
-      Instruction instruction = iterator.next();
-      if (!instruction.isInvokeMethod()) {
-        continue;
-      }
-      InvokeMethod invoke = instruction.asInvokeMethod();
-      DexMethod invokedMethod = invoke.getInvokedMethod();
-      if (invokedMethod.isIdenticalTo(enumValueOfMethod)
-          || invokedMethod.isIdenticalTo(enumMapConstructor)
-          || isEnumSetFactoryMethod(invokedMethod)) {
-        handleReflectiveEnumInvoke(method, invoke, 0);
-      } else if (invokedMethod.isIdenticalTo(bundleGetSerializable2)
-          || invokedMethod.isIdenticalTo(parcelReadSerializable2)
-          || invokedMethod.isIdenticalTo(intentGetSerializableExtra2)) {
-        handleReflectiveEnumInvoke(method, invoke, 1);
-      } else if (invokedMethod.isIdenticalTo(bundleGetSerializable1)
-          || invokedMethod.isIdenticalTo(parcelReadSerializable1)
-          || invokedMethod.isIdenticalTo(intentGetSerializableExtra1)
-          || invokedMethod.isIdenticalTo(objectInputStreamReadObject)) {
-        // These forms do not take a Class parameter, but are often followed by a checked-cast.
-        // Get the enum type from the checked-cast.
-        // There is no checked-cast when the return value is used as an Object (e.g. toString(), or
-        // inserted into a List<Object>).
-        // This also fails to identify when the above methods are wrapped in a helper method
-        // (e.g. a "IntentUtils.safeGetSerializableExtra()").
-        handleEnumCastFromSerializable(method, invoke.outValue());
-      }
-    }
-  }
-
-  private boolean isEnumSetFactoryMethod(DexMethod invokedMethod) {
-    if (!invokedMethod.getHolderType().equals(javaUtilEnumSetType)) {
-      return false;
-    }
-    DexString name = invokedMethod.getName();
-    return name.isIdenticalTo(enumSetAllOfString)
-        || name.isIdenticalTo(enumSetNoneOfString)
-        || name.isIdenticalTo(enumSetOfString)
-        || name.isIdenticalTo(enumSetRangeString);
-  }
-
-  @Override
-  public void traceInvokeStatic(
-      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
-    if (invokedMethod.isIdenticalTo(enumValueOfMethod) || isEnumSetFactoryMethod(invokedMethod)) {
-      pendingReflectiveUses.add(context);
-    }
-  }
-
-  @Override
-  public void traceInvokeDirect(
-      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
-    // EnumMap uses reflection.
-    if (invokedMethod.isIdenticalTo(enumMapConstructor)) {
-      pendingReflectiveUses.add(context);
-    }
-  }
-
-  @Override
-  public void traceInvokeVirtual(
-      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
-    if (invokedMethod.isIdenticalTo(bundleGetSerializable1)
-        || invokedMethod.isIdenticalTo(bundleGetSerializable2)
-        || invokedMethod.isIdenticalTo(parcelReadSerializable1)
-        || invokedMethod.isIdenticalTo(parcelReadSerializable2)
-        || invokedMethod.isIdenticalTo(intentGetSerializableExtra1)
-        || invokedMethod.isIdenticalTo(intentGetSerializableExtra2)
-        || invokedMethod.isIdenticalTo(objectInputStreamReadObject)) {
-      pendingReflectiveUses.add(context);
-    }
-  }
-
-  @Override
-  public void notifyFixpoint(
-      Enqueuer enqueuer,
-      EnqueuerWorklist worklist,
-      ExecutorService executorService,
-      Timing timing) {
-    if (!pendingReflectiveUses.isEmpty()) {
-      timing.begin("Handle reflective Enum operations");
-      pendingReflectiveUses.forEach(this::handleReflectiveBehavior);
-      pendingReflectiveUses.clear();
-      timing.end();
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 44d0ad2..64de864 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -770,8 +770,6 @@
       System.getProperty("com.android.tools.r8.ignoreBootClasspathEnumsForMaindexTracing") != null;
   public boolean pruneNonVissibleAnnotationClasses =
       System.getProperty("com.android.tools.r8.pruneNonVissibleAnnotationClasses") != null;
-  public boolean experimentalTraceEnumReflection =
-      System.getProperty("com.android.tools.r8.experimentalTraceEnumReflection") != null;
 
   // Flag to turn on/offLoad/store optimization in the Cf back-end.
   public boolean enableLoadStoreOptimization = true;
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
index dfd0f1a..01a5e94 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
@@ -51,14 +51,7 @@
             .addInnerClasses(FailingMethodEnumUnboxingTest.class)
             .addKeepMainRules(TESTS)
             .addKeepRules(enumKeepRules.getKeepRules())
-            .addOptionsModification(
-                opt -> {
-                  enableEnumOptions(opt, enumValueOptimization);
-                  if (enumKeepRules == EnumKeepRules.NONE) {
-                    // Look for Enum.valueOf() when tracing rather than rely on -keeps.
-                    opt.experimentalTraceEnumReflection = true;
-                  }
-                })
+            .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
             .addEnumUnboxingInspector(
                 inspector ->
                     inspector.assertNotUnboxed(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
index 4662a90..e75d109 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
@@ -39,14 +39,7 @@
         .addEnumUnboxingInspector(inspector -> inspector.assertNotUnboxed(Main.Enum.class))
         .enableNeverClassInliningAnnotations()
         .addKeepRules(enumKeepRules.getKeepRules())
-        .addOptionsModification(
-            opt -> {
-              enableEnumOptions(opt, enumValueOptimization);
-              if (enumKeepRules == EnumKeepRules.NONE) {
-                // Look for Enum.valueOf() when tracing rather than rely on -keeps.
-                opt.experimentalTraceEnumReflection = true;
-              }
-            })
+        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
         .setMinApi(parameters)
         .compile()
         .run(parameters.getRuntime(), success)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
index e73cda0..f976adb 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
@@ -46,14 +46,7 @@
                 inspector -> inspector.assertUnboxed(EnumValueOf.MyEnum.class))
             .enableNeverClassInliningAnnotations()
             .addKeepRules(enumKeepRules.getKeepRules())
-            .addOptionsModification(
-                opt -> {
-                  enableEnumOptions(opt, enumValueOptimization);
-                  if (enumKeepRules == EnumKeepRules.NONE) {
-                    // Look for Enum.valueOf() when tracing rather than rely on -keeps.
-                    opt.experimentalTraceEnumReflection = true;
-                  }
-                })
+            .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
             .setMinApi(parameters)
             .compile();
     for (Class<?> main : TESTS) {
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinification.java b/src/test/java/com/android/tools/r8/naming/EnumMinification.java
index c72b68e..5b1d6f5 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinification.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinification.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase.EnumKeepRules;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import org.junit.Test;
@@ -51,7 +50,6 @@
         .addProgramClassFileData(enumClassFile)
         .addKeepMainRule(mainClass)
         .addKeepRules("-neverinline enum * extends java.lang.Enum { valueOf(...); }")
-        .addKeepRules(EnumKeepRules.STUDIO.getKeepRules())
         .enableProguardTestOptions()
         .setMinApi(parameters)
         .compile();
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java
index 117154a..fe575ed 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java
@@ -49,13 +49,6 @@
         .addInnerClasses(EnumValueOfOptimizationTest.class)
         .addKeepMainRule(Main.class)
         .addKeepRules(enumKeepRules.getKeepRules())
-        .addOptionsModification(
-            opt -> {
-              if (enumKeepRules == EnumKeepRules.NONE) {
-                // Look for Enum.valueOf() when tracing rather than rely on -keeps.
-                opt.experimentalTraceEnumReflection = true;
-              }
-            })
         .applyIf(
             enableNoVerticalClassMergingAnnotations,
             R8TestBuilder::enableNoVerticalClassMergingAnnotations,
diff --git a/src/test/java/com/android/tools/r8/shaking/enums/EnumCollectionsTest.java b/src/test/java/com/android/tools/r8/shaking/enums/EnumCollectionsTest.java
new file mode 100644
index 0000000..2a9872c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/enums/EnumCollectionsTest.java
@@ -0,0 +1,208 @@
+// Copyright (c) 2024, 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.shaking.enums;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EnumCollectionsTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withMaximumApiLevel().build();
+  }
+
+  private static final List<String> EXPECTED_OUTPUT =
+      Arrays.asList(
+          "none: [A, B]",
+          "all: [B, C]",
+          "of: [C]",
+          "of: [D, E]",
+          "of: [E, F, G]",
+          "of: [F, G, H, I]",
+          "of: [G, H, I, J, K]",
+          "of: [H, I, J, K, L, M]",
+          "range: [I, J]",
+          "map: {J=1}",
+          "valueOf: K",
+          "phi: [B]");
+
+  public static class TestMain {
+    public enum EnumA {
+      A,
+      B
+    }
+
+    public enum EnumB {
+      B,
+      C
+    }
+
+    public enum EnumC {
+      C,
+      D
+    }
+
+    public enum EnumD {
+      D,
+      E
+    }
+
+    public enum EnumE {
+      E,
+      F,
+      G
+    }
+
+    public enum EnumF {
+      F,
+      G,
+      H,
+      I
+    }
+
+    public enum EnumG {
+      G,
+      H,
+      I,
+      J,
+      K
+    }
+
+    public enum EnumH {
+      H,
+      I,
+      J,
+      K,
+      L,
+      M
+    }
+
+    public enum EnumI {
+      I,
+      J
+    }
+
+    public enum EnumJ {
+      J,
+      K
+    }
+
+    public enum EnumK {
+      K,
+      L
+    }
+
+    @NeverInline
+    private static void noneOf() {
+      System.out.println("none: " + EnumSet.complementOf(EnumSet.noneOf(EnumA.class)));
+    }
+
+    @NeverInline
+    private static void allOf() {
+      System.out.println("all: " + EnumSet.allOf(EnumB.class));
+    }
+
+    @NeverInline
+    private static void of1() {
+      System.out.println("of: " + EnumSet.of(EnumC.C));
+    }
+
+    @NeverInline
+    private static void of2() {
+      System.out.println("of: " + EnumSet.of(EnumD.D, EnumD.E));
+    }
+
+    @NeverInline
+    private static void of3() {
+      System.out.println("of: " + EnumSet.of(EnumE.E, EnumE.F, EnumE.G));
+    }
+
+    @NeverInline
+    private static void of4() {
+      System.out.println("of: " + EnumSet.of(EnumF.F, EnumF.G, EnumF.H, EnumF.I));
+    }
+
+    @NeverInline
+    private static void of5() {
+      System.out.println("of: " + EnumSet.of(EnumG.G, EnumG.H, EnumG.I, EnumG.J, EnumG.K));
+    }
+
+    @NeverInline
+    private static void ofVarArgs() {
+      System.out.println("of: " + EnumSet.of(EnumH.H, EnumH.I, EnumH.J, EnumH.K, EnumH.L, EnumH.M));
+    }
+
+    @NeverInline
+    private static void range() {
+      System.out.println("range: " + EnumSet.range(EnumI.I, EnumI.J));
+    }
+
+    @NeverInline
+    private static void map() {
+      EnumMap<EnumJ, Integer> map = new EnumMap<>(EnumJ.class);
+      map.put(EnumJ.J, 1);
+      System.out.println("map: " + map);
+    }
+
+    @NeverInline
+    private static void valueOf() {
+      System.out.println("valueOf: " + EnumK.valueOf("K"));
+    }
+
+    public static void main(String[] args) {
+      // Use different methods to ensure Enqueuer.traceInvokeStatic() triggers for each one.
+      noneOf();
+      allOf();
+      of1();
+      of2();
+      of3();
+      of4();
+      of5();
+      ofVarArgs();
+      range();
+      map();
+      valueOf();
+      // Ensure phi as argument does not cause issues.
+      System.out.println(
+          "phi: " + EnumSet.of((Enum) (args.length > 10 ? (Object) EnumA.A : (Object) EnumB.B)));
+    }
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClassesAndInnerClasses(TestMain.class)
+        .run(parameters.getRuntime(), TestMain.class)
+        .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClassesAndInnerClasses(TestMain.class)
+        .enableInliningAnnotations()
+        .addKeepMainRule(TestMain.class)
+        .compile()
+        .run(parameters.getRuntime(), TestMain.class)
+        .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/enums/EnumReflectionTest.java b/src/test/java/com/android/tools/r8/shaking/enums/EnumReflectionTest.java
deleted file mode 100644
index 9e9b297..0000000
--- a/src/test/java/com/android/tools/r8/shaking/enums/EnumReflectionTest.java
+++ /dev/null
@@ -1,505 +0,0 @@
-// 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.shaking.enums;
-
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumA;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumB;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumC;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumD;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumE;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumF;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumG;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumH;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumI;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumJ;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumK;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumL;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumM;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumN;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumO;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumP;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumQ;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumR;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumS;
-import com.android.tools.r8.shaking.enums.EnumReflectionTest.Helpers.EnumT;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-/** Tests for the various APIs that cause an Enum type's values() method to be kept. */
-@RunWith(Parameterized.class)
-public class EnumReflectionTest extends TestBase {
-
-  @Parameter(0)
-  public TestParameters parameters;
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withDefaultRuntimes().withMaximumApiLevel().build();
-  }
-
-  public static class FakeParcel {
-    // Deserializing arrays yields UnsatisfiedLinkError for com.android.org.conscrypt.NativeCrypto
-    // when running under ART.
-    private Class arrayType;
-    private ObjectInputStream objectInputStream;
-
-    public static byte[] toBytes(Serializable thing) {
-      try {
-        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
-        objectOutputStream.writeObject(thing);
-        return byteArrayOutputStream.toByteArray();
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    public static FakeParcel createWithSingleSerializable(Serializable thing) {
-      try {
-        FakeParcel ret = new FakeParcel();
-        if (thing.getClass().isArray()) {
-          ret.arrayType = thing.getClass();
-          if (Array.getLength(thing) != 1) {
-            throw new IllegalArgumentException();
-          }
-          thing = (Serializable) Array.get(thing, 0);
-        }
-        ret.objectInputStream = new ObjectInputStream(new ByteArrayInputStream(toBytes(thing)));
-        return ret;
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    public Serializable readSerializable() {
-      try {
-        Serializable ret = (Serializable) objectInputStream.readObject();
-        if (arrayType != null) {
-          Object array = Array.newInstance(arrayType.getComponentType(), 1);
-          Array.set(array, 0, ret);
-          ret = (Serializable) array;
-        }
-        return ret;
-      } catch (Exception e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    public <T extends Serializable> T readSerializable(ClassLoader loader, Class<T> clazz) {
-      return clazz.cast(readSerializable());
-    }
-  }
-
-  public static class FakeBundle {
-    private Object parcel;
-
-    public static FakeBundle createWithSingleSerializable(Serializable thing) {
-      FakeBundle ret = new FakeBundle();
-      ret.parcel = FakeParcel.createWithSingleSerializable(thing);
-      return ret;
-    }
-
-    public <T extends Serializable> T getSerializable(String key, Class<T> clazz) {
-      return clazz.cast(getSerializable(key));
-    }
-
-    public Serializable getSerializable(String key) {
-      return ((FakeParcel) parcel).readSerializable();
-    }
-  }
-
-  public static class FakeIntent {
-    private Object parcel;
-
-    public static FakeIntent createWithSingleSerializable(Serializable thing) {
-      FakeIntent ret = new FakeIntent();
-      ret.parcel = FakeParcel.createWithSingleSerializable(thing);
-      return ret;
-    }
-
-    public <T extends Serializable> T getSerializableExtra(String key, Class<T> clazz) {
-      return clazz.cast(getSerializableExtra(key));
-    }
-
-    public Serializable getSerializableExtra(String key) {
-      return ((FakeParcel) parcel).readSerializable();
-    }
-  }
-
-  private static final List<String> EXPECTED_OUTPUT =
-      Arrays.asList(
-          "none: [A, B]",
-          "all: [B, C]",
-          "of: [C]",
-          "of: [D, E]",
-          "of: [E, F, G]",
-          "of: [F, G, H, I]",
-          "of: [G, H, I, J, K]",
-          "of: [H, I, J, K, L, M]",
-          "range: [I, J]",
-          "map: {J=1}",
-          "valueOf: K",
-          "bundle: L",
-          "bundle: M",
-          "parcel: N",
-          "parcel: O",
-          "intent: P",
-          "intent: Q",
-          "stream: R",
-          "array: [S]",
-          "array: [T]",
-          "phi: [B]");
-
-  public static class Helpers {
-
-    public enum EnumA {
-      A,
-      B
-    }
-
-    public enum EnumB {
-      B,
-      C
-    }
-
-    public enum EnumC {
-      C,
-      D
-    }
-
-    public enum EnumD {
-      D,
-      E
-    }
-
-    public enum EnumE {
-      E,
-      F,
-      G
-    }
-
-    public enum EnumF {
-      F,
-      G,
-      H,
-      I
-    }
-
-    public enum EnumG {
-      G,
-      H,
-      I,
-      J,
-      K
-    }
-
-    public enum EnumH {
-      H,
-      I,
-      J,
-      K,
-      L,
-      M
-    }
-
-    public enum EnumI {
-      I,
-      J
-    }
-
-    public enum EnumJ {
-      J,
-      K
-    }
-
-    public enum EnumK {
-      K,
-      L
-    }
-
-    public enum EnumL {
-      L,
-      M
-    }
-
-    public enum EnumM {
-      M,
-      N
-    }
-
-    public enum EnumN {
-      N,
-      O
-    }
-
-    public enum EnumO {
-      O,
-      P
-    }
-
-    public enum EnumP {
-      P,
-      Q
-    }
-
-    public enum EnumQ {
-      Q,
-      R
-    }
-
-    public enum EnumR {
-      R {}, // Test anonymous enum subtype.
-      S
-    }
-
-    public enum EnumS {
-      S,
-      T
-    }
-
-    public enum EnumT {
-      T,
-      U
-    }
-  }
-
-  public static class TestMain {
-    @NeverInline
-    private static void noneOf() {
-      System.out.println("none: " + EnumSet.complementOf(EnumSet.noneOf(EnumA.class)));
-    }
-
-    @NeverInline
-    private static void allOf() {
-      System.out.println("all: " + EnumSet.allOf(EnumB.class));
-    }
-
-    @NeverInline
-    private static void of1() {
-      System.out.println("of: " + EnumSet.of(EnumC.C));
-    }
-
-    @NeverInline
-    private static void of2() {
-      System.out.println("of: " + EnumSet.of(EnumD.D, EnumD.E));
-    }
-
-    @NeverInline
-    private static void of3() {
-      System.out.println("of: " + EnumSet.of(EnumE.E, EnumE.F, EnumE.G));
-    }
-
-    @NeverInline
-    private static void of4() {
-      System.out.println("of: " + EnumSet.of(EnumF.F, EnumF.G, EnumF.H, EnumF.I));
-    }
-
-    @NeverInline
-    private static void of5() {
-      System.out.println("of: " + EnumSet.of(EnumG.G, EnumG.H, EnumG.I, EnumG.J, EnumG.K));
-    }
-
-    @NeverInline
-    private static void ofVarArgs() {
-      System.out.println("of: " + EnumSet.of(EnumH.H, EnumH.I, EnumH.J, EnumH.K, EnumH.L, EnumH.M));
-    }
-
-    @NeverInline
-    private static void range() {
-      System.out.println("range: " + EnumSet.range(EnumI.I, EnumI.J));
-    }
-
-    @NeverInline
-    private static void map() {
-      EnumMap<EnumJ, Integer> map = new EnumMap<>(EnumJ.class);
-      map.put(EnumJ.J, 1);
-      System.out.println("map: " + map);
-    }
-
-    @NeverInline
-    private static void valueOf() {
-      System.out.println("valueOf: " + EnumK.valueOf("K"));
-    }
-
-    @NeverInline
-    private static void androidBundle1() {
-      FakeBundle b = FakeBundle.createWithSingleSerializable(EnumL.L);
-      EnumL result = (EnumL) b.getSerializable("");
-      System.out.println("bundle: " + result);
-    }
-
-    @NeverInline
-    private static void androidBundle2() {
-      FakeBundle b = FakeBundle.createWithSingleSerializable(EnumM.M);
-      EnumM result = b.getSerializable("", EnumM.class);
-      System.out.println("bundle: " + result);
-    }
-
-    @NeverInline
-    private static void androidParcel1() {
-      FakeParcel p = FakeParcel.createWithSingleSerializable(EnumN.N);
-      EnumN result = (EnumN) p.readSerializable();
-      System.out.println("parcel: " + result);
-    }
-
-    @NeverInline
-    private static void androidParcel2() {
-      FakeParcel p = FakeParcel.createWithSingleSerializable(EnumO.O);
-      System.out.println("parcel: " + p.readSerializable(null, EnumO.class));
-    }
-
-    @NeverInline
-    private static void androidIntent1() {
-      FakeIntent i = FakeIntent.createWithSingleSerializable(EnumP.P);
-      EnumP result = (EnumP) i.getSerializableExtra("");
-      System.out.println("intent: " + result);
-    }
-
-    @NeverInline
-    private static void androidIntent2() {
-      FakeIntent i = FakeIntent.createWithSingleSerializable(EnumQ.Q);
-      System.out.println("intent: " + i.getSerializableExtra("", EnumQ.class));
-    }
-
-    @NeverInline
-    private static void array1() {
-      FakeParcel p = FakeParcel.createWithSingleSerializable(new EnumS[] {EnumS.S});
-      EnumS[] result = (EnumS[]) p.readSerializable();
-      System.out.println("array: " + Arrays.toString(result));
-    }
-
-    @NeverInline
-    private static void array2() {
-      FakeParcel p = FakeParcel.createWithSingleSerializable(new EnumT[] {EnumT.T});
-      System.out.println("array: " + Arrays.toString(p.readSerializable(null, EnumT[].class)));
-    }
-
-    @NeverInline
-    private static void objectStream() {
-      try {
-        ObjectInputStream objectInputStream =
-            new ObjectInputStream(new ByteArrayInputStream(FakeParcel.toBytes(EnumR.R)));
-        EnumR result = (EnumR) objectInputStream.readObject();
-        System.out.println("stream: " + result);
-      } catch (Exception e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    public static void main(String[] args) {
-      // Use different methods to ensure Enqueuer.traceInvokeStatic() triggers for each one.
-      noneOf();
-      allOf();
-      of1();
-      of2();
-      of3();
-      of4();
-      of5();
-      ofVarArgs();
-      range();
-      map();
-      valueOf();
-      androidBundle1();
-      androidBundle2();
-      androidParcel1();
-      androidParcel2();
-      androidIntent1();
-      androidIntent2();
-      objectStream();
-      array1();
-      array2();
-      // Ensure phi as argument does not cause issues.
-      System.out.println(
-          "phi: " + EnumSet.of((Enum) (args.length > 10 ? (Object) EnumA.A : (Object) EnumB.B)));
-    }
-  }
-
-  // EnumR.R references this class in its constructor.
-  private static final String ENUM_SUBTYPE_BRIDGE_CLASS_NAME =
-      EnumReflectionTest.class.getName() + "$1";
-
-  private static final String PARCEL_DESCRIPTOR = "Landroid/os/Parcel;";
-  private static final String BUNDLE_DESCRIPTOR = "Landroid/os/Bundle;";
-  private static final String INTENT_DESCRIPTOR = "Landroid/content/Intent;";
-
-  private static byte[] rewriteTestMain() throws IOException {
-    return transformer(TestMain.class)
-        .replaceClassDescriptorInMethodInstructions(descriptor(FakeParcel.class), PARCEL_DESCRIPTOR)
-        .replaceClassDescriptorInMethodInstructions(descriptor(FakeBundle.class), BUNDLE_DESCRIPTOR)
-        .replaceClassDescriptorInMethodInstructions(descriptor(FakeIntent.class), INTENT_DESCRIPTOR)
-        .transform();
-  }
-
-  private static byte[] rewriteParcel() throws IOException {
-    return transformer(FakeParcel.class).setClassDescriptor(PARCEL_DESCRIPTOR).transform();
-  }
-
-  private static byte[] rewriteBundle() throws IOException {
-    return transformer(FakeBundle.class)
-        .setClassDescriptor(BUNDLE_DESCRIPTOR)
-        .replaceClassDescriptorInMethodInstructions(descriptor(FakeParcel.class), PARCEL_DESCRIPTOR)
-        .transform();
-  }
-
-  private static byte[] rewriteIntent() throws IOException {
-    return transformer(FakeIntent.class)
-        .setClassDescriptor(INTENT_DESCRIPTOR)
-        .replaceClassDescriptorInMethodInstructions(descriptor(FakeParcel.class), PARCEL_DESCRIPTOR)
-        .transform();
-  }
-
-  @Test
-  public void testRuntime() throws Exception {
-    byte[] parcelBytes = rewriteParcel();
-    byte[] bundleBytes = rewriteBundle();
-    byte[] intentBytes = rewriteIntent();
-    testForRuntime(parameters)
-        .addProgramClassesAndInnerClasses(Helpers.class)
-        .addProgramClassFileData(rewriteTestMain())
-        .addProgramClasses(Class.forName(ENUM_SUBTYPE_BRIDGE_CLASS_NAME))
-        .addClasspathClassFileData(parcelBytes, bundleBytes, intentBytes)
-        .addRunClasspathFiles(buildOnDexRuntime(parameters, parcelBytes, bundleBytes, intentBytes))
-        .run(parameters.getRuntime(), TestMain.class)
-        .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
-  }
-
-  @Test
-  public void testR8() throws Exception {
-    byte[] parcelBytes = rewriteParcel();
-    byte[] bundleBytes = rewriteBundle();
-    byte[] intentBytes = rewriteIntent();
-    testForR8(parameters.getBackend())
-        .setMinApi(parameters)
-        .addOptionsModification(options -> options.experimentalTraceEnumReflection = true)
-        .addProgramClassesAndInnerClasses(Helpers.class)
-        .addProgramClassFileData(rewriteTestMain())
-        .addProgramClasses(Class.forName(ENUM_SUBTYPE_BRIDGE_CLASS_NAME))
-        .addClasspathClassFileData(parcelBytes, bundleBytes, intentBytes)
-        .enableInliningAnnotations()
-        .addKeepMainRule(TestMain.class)
-        .compile()
-        .addRunClasspathClassFileData(parcelBytes, bundleBytes, intentBytes)
-        .run(parameters.getRuntime(), TestMain.class)
-        .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
-  }
-}