Improve InvokeDynamic and ConstDynamic modeling

Bug: b/336510513
Change-Id: I7c0eca175e5c83fd5f9c88e168cfd0d94393b28d
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 c16eae4..9936ef6 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
@@ -31,9 +31,10 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.HashingVisitor;
-import java.util.ArrayList;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.function.Supplier;
 import org.objectweb.asm.ConstantDynamic;
 import org.objectweb.asm.MethodVisitor;
 
@@ -41,22 +42,8 @@
 
   private final ConstantDynamicReference reference;
 
-  public CfConstDynamic(
-      int symbolicReferenceId,
-      DexString name,
-      DexType type,
-      DexMethodHandle bootstrapMethod,
-      List<DexValue> bootstrapMethodArguments) {
-    assert symbolicReferenceId >= 0;
-    assert name != null;
-    assert type != null;
-    assert bootstrapMethod != null;
-    assert bootstrapMethodArguments != null;
-    assert bootstrapMethodArguments.isEmpty();
-
-    reference =
-        new ConstantDynamicReference(
-            symbolicReferenceId, name, type, bootstrapMethod, bootstrapMethodArguments);
+  public CfConstDynamic(ConstantDynamicReference constantDynamicReference) {
+    reference = constantDynamicReference;
   }
 
   @Override
@@ -86,27 +73,14 @@
   }
 
   public static CfConstDynamic fromAsmConstantDynamic(
-      int symbolicReferenceId,
       ConstantDynamic insn,
       JarApplicationReader application,
-      DexType clazz) {
-    String constantName = insn.getName();
-    String constantDescriptor = insn.getDescriptor();
-    DexMethodHandle bootstrapMethodHandle =
-        DexMethodHandle.fromAsmHandle(insn.getBootstrapMethod(), application, clazz);
-    int argumentCount = insn.getBootstrapMethodArgumentCount();
-    List<DexValue> bootstrapMethodArguments = new ArrayList<>(argumentCount);
-    for (int i = 0; i < argumentCount; i++) {
-      Object argument = insn.getBootstrapMethodArgument(i);
-      DexValue dexValue = DexValue.fromAsmBootstrapArgument(argument, application, clazz);
-      bootstrapMethodArguments.add(dexValue);
-    }
+      DexType clazz,
+      Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
+    assert insn.getBootstrapMethodArgumentCount() == 0;
     return new CfConstDynamic(
-        symbolicReferenceId,
-        application.getString(constantName),
-        application.getTypeFromDescriptor(constantDescriptor),
-        bootstrapMethodHandle,
-        bootstrapMethodArguments);
+        ConstantDynamicReference.fromAsmConstantDynamic(
+            insn, application, clazz, constantDynamicSymbolicReferencesSupplier));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index c09e32a..2cf72f1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import com.google.common.io.BaseEncoding;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.ObjectOutputStream;
@@ -24,9 +25,10 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Supplier;
+import org.objectweb.asm.ConstantDynamic;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.InvokeDynamicInsnNode;
 
 public final class DexCallSite extends IndexedDexItem
     implements StructuralItem<DexCallSite>, LirConstant {
@@ -76,17 +78,13 @@
   }
 
   public static DexCallSite fromAsmInvokeDynamic(
-      InvokeDynamicInsnNode insn, JarApplicationReader application, DexType clazz) {
-    return fromAsmInvokeDynamic(application, clazz, insn.name, insn.desc, insn.bsm, insn.bsmArgs);
-  }
-
-  public static DexCallSite fromAsmInvokeDynamic(
       JarApplicationReader application,
       DexType clazz,
       String name,
       String desc,
       Handle bsmHandle,
-      Object[] bsmArgs) {
+      Object[] bsmArgs,
+      Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
     // Bootstrap method
     if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC
         && bsmHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
@@ -99,7 +97,9 @@
     // Decode static bootstrap arguments
     List<DexValue> bootstrapArgs = new ArrayList<>();
     for (Object arg : bsmArgs) {
-      bootstrapArgs.add(DexValue.fromAsmBootstrapArgument(arg, application, clazz));
+      bootstrapArgs.add(
+          DexValue.fromAsmBootstrapArgument(
+              arg, application, clazz, constantDynamicSymbolicReferencesSupplier));
     }
 
     // Construct call site
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 38b5634..f4f7a60 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.dex.FileWriter;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -19,6 +20,7 @@
 import com.android.tools.r8.ir.code.DexItemBasedConstString;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicReference;
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.EncodedValueUtils;
@@ -26,8 +28,11 @@
 import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.Arrays;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
+import org.objectweb.asm.ConstantDynamic;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Type;
 
@@ -51,7 +56,8 @@
     ARRAY(0x1c),
     ANNOTATION(0x1d),
     NULL(0x1e),
-    BOOLEAN(0x1f);
+    BOOLEAN(0x1f),
+    CONST_DYNAMIC(-1);
 
     public static DexValueKind fromId(int id) {
       switch (id) {
@@ -91,6 +97,8 @@
           return NULL;
         case 0x1f:
           return BOOLEAN;
+        case -1:
+          return CONST_DYNAMIC;
         default:
           throw new Unreachable();
       }
@@ -162,6 +170,14 @@
     return null;
   }
 
+  public boolean isDexValueConstDynamic() {
+    return false;
+  }
+
+  public DexValueConstDynamic asDexValueConstDynamic() {
+    return null;
+  }
+
   public boolean isDexValueMethodType() {
     return false;
   }
@@ -313,7 +329,10 @@
   public abstract AbstractValue toAbstractValue(AbstractValueFactory factory);
 
   public static DexValue fromAsmBootstrapArgument(
-      Object value, JarApplicationReader application, DexType clazz) {
+      Object value,
+      JarApplicationReader application,
+      DexType clazz,
+      Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
     if (value instanceof Integer) {
       return DexValue.DexValueInt.create((Integer) value);
     } else if (value instanceof Long) {
@@ -334,12 +353,23 @@
         case Type.METHOD:
           return new DexValue.DexValueMethodType(
               application.getProto(((Type) value).getDescriptor()));
+        case Type.ARRAY:
+          DexType arrayType = application.getTypeFromDescriptor(((Type) value).getDescriptor());
+          assert arrayType.isArrayType();
+          return new DexValue.DexValueType(arrayType);
         default:
           throw new Unreachable("Type sort is not supported: " + type.getSort());
       }
     } else if (value instanceof Handle) {
       return new DexValue.DexValueMethodHandle(
           DexMethodHandle.fromAsmHandle((Handle) value, application, clazz));
+    } else if (value instanceof ConstantDynamic) {
+      return new DexValue.DexValueConstDynamic(
+          ConstantDynamicReference.fromAsmConstantDynamic(
+              (ConstantDynamic) value,
+              application,
+              clazz,
+              constantDynamicSymbolicReferencesSupplier));
     } else {
       throw new Unreachable(
           "Unsupported bootstrap static argument of type " + value.getClass().getSimpleName());
@@ -2054,4 +2084,96 @@
       return UnknownValue.getInstance();
     }
   }
+
+  public static class DexValueConstDynamic extends DexValue {
+
+    private final ConstantDynamicReference value;
+
+    public DexValueConstDynamic(ConstantDynamicReference value) {
+      this.value = value;
+    }
+
+    @Override
+    public boolean isDexValueConstDynamic() {
+      return true;
+    }
+
+    @Override
+    public DexValueConstDynamic asDexValueConstDynamic() {
+      return this;
+    }
+
+    @Override
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueConstDynamic().value, visitor);
+    }
+
+    @Override
+    void internalAcceptHashing(HashingVisitor visitor) {
+      value.acceptHashing(visitor);
+    }
+
+    @Override
+    public DexValueKind getValueKind() {
+      return DexValueKind.CONST_DYNAMIC;
+    }
+
+    private CompilationError throwCannotConvertToDex() {
+      throw new CompilationError("DexValueConstDynamic should be desugared");
+    }
+
+    @Override
+    public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) {
+      throw throwCannotConvertToDex();
+    }
+
+    @Override
+    public void sort() {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      throw throwCannotConvertToDex();
+    }
+
+    @Override
+    public int hashCode() {
+      return 7 * value.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other instanceof DexValueConstDynamic) {
+        DexValueConstDynamic otherCstDynamic = (DexValueConstDynamic) other;
+        return otherCstDynamic.value.equals(value);
+      }
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return "Item " + getValueKind() + " " + value;
+    }
+
+    @Override
+    public DexType getType(DexItemFactory factory) {
+      return null;
+    }
+
+    @Override
+    public Object getBoxedValue() {
+      throw new Unreachable("No boxed value for DexValueConstDynamic");
+    }
+
+    @Override
+    public Object asAsmEncodedObject() {
+      throw new Unreachable("No ASM conversion for DexValueConstDynamic");
+    }
+
+    @Override
+    public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+      return UnknownValue.getInstance();
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 2ad9817..c41f8df 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -921,7 +921,14 @@
     @Override
     public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
       DexCallSite callSite =
-          DexCallSite.fromAsmInvokeDynamic(application, method.holder, name, desc, bsm, bsmArgs);
+          DexCallSite.fromAsmInvokeDynamic(
+              application,
+              method.holder,
+              name,
+              desc,
+              bsm,
+              bsmArgs,
+              constantDynamicSymbolicReferencesSupplier);
       addInstruction(new CfInvokeDynamic(callSite));
     }
 
@@ -1027,16 +1034,12 @@
         // instance from ASM, even when they are equal (i.e. all their components are equal). See
         // ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest
         // for an example.
-        Reference2IntMap<ConstantDynamic> constantDynamicSymbolicReferences =
-            constantDynamicSymbolicReferencesSupplier.get();
-        int symbolicReferenceId = constantDynamicSymbolicReferences.getOrDefault(cst, -1);
-        if (symbolicReferenceId == -1) {
-          symbolicReferenceId = constantDynamicSymbolicReferences.size();
-          constantDynamicSymbolicReferences.put((ConstantDynamic) cst, symbolicReferenceId);
-        }
         addInstruction(
             CfConstDynamic.fromAsmConstantDynamic(
-                symbolicReferenceId, (ConstantDynamic) cst, application, method.holder));
+                (ConstantDynamic) cst,
+                application,
+                method.holder,
+                constantDynamicSymbolicReferencesSupplier));
       } else {
         throw new CompilationError("Unsupported constant: " + cst.toString());
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
index 478e4cf..2f27eab 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
@@ -7,12 +7,17 @@
 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.graph.JarApplicationReader;
 import com.android.tools.r8.utils.structural.Equatable;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Supplier;
+import org.objectweb.asm.ConstantDynamic;
 
 public class ConstantDynamicReference implements StructuralItem<ConstantDynamicReference> {
   private final int symbolicReferenceId;
@@ -25,13 +30,53 @@
     spec.withInt(c -> c.symbolicReferenceId);
   }
 
-  public ConstantDynamicReference(
+  public static ConstantDynamicReference fromAsmConstantDynamic(
+      ConstantDynamic cst,
+      JarApplicationReader application,
+      DexType clazz,
+      Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
+
+    Reference2IntMap<ConstantDynamic> constantDynamicSymbolicReferences =
+        constantDynamicSymbolicReferencesSupplier.get();
+    int symbolicReferenceId = constantDynamicSymbolicReferences.getOrDefault(cst, -1);
+    if (symbolicReferenceId == -1) {
+      symbolicReferenceId = constantDynamicSymbolicReferences.size();
+      constantDynamicSymbolicReferences.put(cst, symbolicReferenceId);
+    }
+
+    String constantName = cst.getName();
+    String constantDescriptor = cst.getDescriptor();
+    DexMethodHandle bootstrapMethodHandle =
+        DexMethodHandle.fromAsmHandle(cst.getBootstrapMethod(), application, clazz);
+    int argumentCount = cst.getBootstrapMethodArgumentCount();
+    List<DexValue> bootstrapMethodArguments1 = new ArrayList<>(argumentCount);
+    for (int i = 0; i < argumentCount; i++) {
+      Object argument = cst.getBootstrapMethodArgument(i);
+      DexValue dexValue =
+          DexValue.fromAsmBootstrapArgument(
+              argument, application, clazz, constantDynamicSymbolicReferencesSupplier);
+      bootstrapMethodArguments1.add(dexValue);
+    }
+
+    return new ConstantDynamicReference(
+        symbolicReferenceId,
+        application.getString(constantName),
+        application.getTypeFromDescriptor(constantDescriptor),
+        bootstrapMethodHandle,
+        bootstrapMethodArguments1);
+  }
+
+  private ConstantDynamicReference(
       int symbolicReferenceId,
       DexString name,
       DexType type,
       DexMethodHandle bootstrapMethod,
       List<DexValue> bootstrapMethodArguments) {
-    assert bootstrapMethodArguments.isEmpty();
+    assert symbolicReferenceId >= 0;
+    assert name != null;
+    assert type != null;
+    assert bootstrapMethod != null;
+    assert bootstrapMethodArguments != null;
     this.symbolicReferenceId = symbolicReferenceId;
     this.name = name;
     this.type = type;
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
index 07f10ad..90001a5 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -41,38 +40,31 @@
     CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
     // javac generated an invokedynamic using bootstrap method
     // java.lang.runtime.SwitchBootstraps.typeSwitch.
-    try {
-      assertEquals(
-          1,
-          inspector
-              .clazz(Main.class)
-              .uniqueMethodWithOriginalName("enumSwitch")
-              .streamInstructions()
-              .filter(InstructionSubject::isInvokeDynamic)
-              .map(
-                  instruction ->
-                      instruction
-                          .asCfInstruction()
-                          .getInstruction()
-                          .asInvokeDynamic()
-                          .getCallSite()
-                          .getBootstrapMethod()
-                          .member
-                          .asDexMethod())
-              .filter(
-                  method ->
-                      method
-                          .getHolderType()
-                          .toString()
-                          .contains("java.lang.runtime.SwitchBootstraps"))
-              .filter(method -> method.toString().contains("typeSwitch"))
-              .count());
-    } catch (CompilationError e) {
-      assertEquals("Could not parse code", e.getMessage());
-      assertEquals(
-          "Unsupported bootstrap static argument of type ConstantDynamic",
-          e.getCause().getMessage());
-    }
+    assertEquals(
+        1,
+        inspector
+            .clazz(Main.class)
+            .uniqueMethodWithOriginalName("enumSwitch")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeDynamic)
+            .map(
+                instruction ->
+                    instruction
+                        .asCfInstruction()
+                        .getInstruction()
+                        .asInvokeDynamic()
+                        .getCallSite()
+                        .getBootstrapMethod()
+                        .member
+                        .asDexMethod())
+            .filter(
+                method ->
+                    method
+                        .getHolderType()
+                        .toString()
+                        .contains("java.lang.runtime.SwitchBootstraps"))
+            .filter(method -> method.toString().contains("typeSwitch"))
+            .count());
 
     parameters.assumeJvmTestParameters();
     testForJvm(parameters)
@@ -116,6 +108,7 @@
   static final class C implements I {}
 
   static class Main {
+
     static void enumSwitch(I i) {
       switch (i) {
         case E.E1 -> {
diff --git a/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
index a330d75..144af9d 100644
--- a/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
@@ -104,18 +104,18 @@
         case null -> {
           System.out.println("null");
         }
-        case "y", "Y" -> {
-          System.out.println("y or Y");
-        }
-        case "n", "N" -> {
-          System.out.println("n or N");
-        }
         case String s when s.equalsIgnoreCase("YES") -> {
           System.out.println("yes");
         }
+        case "y", "Y" -> {
+          System.out.println("y or Y");
+        }
         case String s when s.equalsIgnoreCase("NO") -> {
           System.out.println("no");
         }
+        case "n", "N" -> {
+          System.out.println("n or N");
+        }
         case String s -> {
           System.out.println("unknown");
         }
diff --git a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
index 5ad94ba..f272cde 100644
--- a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -48,24 +47,19 @@
     CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
     // javac generated an invokedynamic using bootstrap method argument of an arrya type (sort 9
     // is org.objectweb.asm.Type.ARRAY).
-    try {
       inspector
           .clazz(Main.class)
           .uniqueMethodWithOriginalName("typeSwitch")
           .streamInstructions()
           .filter(InstructionSubject::isInvokeDynamic)
           .count();
-    } catch (CompilationError e) {
-      assertEquals("Could not parse code", e.getMessage());
-      assertEquals("Type sort is not supported: 9", e.getCause().getMessage());
-    }
     // javac generated an invokedynamic using bootstrap method
     // java.lang.runtime.SwitchBootstraps.typeSwitch.
     assertEquals(
         1,
         inspector
             .clazz(Main.class)
-            .uniqueMethodWithOriginalName("typeSwitchNoArray")
+            .uniqueMethodWithOriginalName("typeSwitch")
             .streamInstructions()
             .filter(InstructionSubject::isInvokeDynamic)
             .map(
@@ -127,17 +121,6 @@
 
   static class Main {
 
-    // No array version only for loading into code inspector.
-    static void typeSwitchNoArray(Object obj) {
-      switch (obj) {
-        case null -> System.out.println("null");
-        case String string -> System.out.println("String");
-        case Color color -> System.out.println("Color: " + color);
-        case Point point -> System.out.println("Record class: " + point);
-        default -> System.out.println("Other");
-      }
-    }
-
     static void typeSwitch(Object obj) {
       switch (obj) {
         case null -> System.out.println("null");