Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8304814

Cannot assign instance of java.util.CollSer to java.util.List

    XMLWordPrintable

Details

    Description

      ADDITIONAL SYSTEM INFORMATION :
      java version "20" 2023-03-21
      Java(TM) SE Runtime Environment (build 20+36-2344)
      Java HotSpot(TM) 64-Bit Server VM (build 20+36-2344, mixed mode, sharing)


      A DESCRIPTION OF THE PROBLEM :
      In a specific condition, I cannot unserialize an object that has references to a List created with List.copyOf().

      I have this exception while unserializing an object:

      java.lang.ClassCastException: cannot assign instance of java.util.CollSer to field a.b.c.BaseClass.aField of type java.util.List in instance of a.b.c.SubClass.

      At serialization, the a.b.c.BaseClass.aField does contain a List that was created like this

      return List.copyOf(originalList);

      I have a “simple” workaround to fix the problem by changing the previous line to:

      return originalList.

      Note that the list may be shared by multiple instances of a.b.c.BaseClass.

      see also:
      https://stackoverflow.com/questions/75804762/classcastexception-when-unserializing-an-immutable-list-cannot-assign-instance
      https://stackoverflow.com/questions/65613929/map-in-activemq-objectmessage-throws-classcastexception-cannot-assign-instance


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      I can reproduce this problem systematically. Unfortunately, this is a huge object being serialized (215 classes, 900MB on disk, error stack trace is 4200 lines). This object has been growing for years. It uses unmodifiable Maps, Lists, Sets ... all over without problems. It is just à this one usage point added recently where using List.copyOf() method causes this exception. I have tried to reproduce this problem using a small test case unsuccessfully.

      Here is the code and state at the point of exception:

      If I break and debug at the exception point (this is JDK 17 code) at java.base/java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2227)

         private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
              if (obj == null) {
                  throw new NullPointerException();
              }
              for (int i = numPrimFields; i < fields.length; i++) {
                  long key = writeKeys[i];
                  if (key == Unsafe.INVALID_FIELD_OFFSET) {
                      continue; // discard value
                  }
                  switch (typeCodes[i]) {
                      case 'L', '[' -> {
                          Object val = vals[offsets[i]];
                          if (val != null &&
                              !types[i - numPrimFields].isInstance(val))
                          {
                              Field f = fields[i].getField();
                              throw new ClassCastException(
                                  "cannot assign instance of " +
                                  val.getClass().getName() + " to field " +
                                  f.getDeclaringClass().getName() + "." +
                                  f.getName() + " of type " +
                                  f.getType().getName() + " in instance of " +
                                  obj.getClass().getName());
                          }
                          if (!dryRun)
                              unsafe.putReference(obj, key, val);
                      }
                      default -> throw new InternalError();
                  }
              }
          }

      In my scenario,

      obj is an instance of a.b.c.SubClass
      vals has 26 elements of which index 17 is a CollSer
      dryRun is true
      The “i” value is 20.
      typecode[i] is ‘L’
      numPrimFields is 3
      offset[i] is 17
      types[i - numPrimFields] is java.util.List
      val is a CollSer (array null, tag 1 (IMM_LIST))

      The line:

      !types[i - numPrimFields].isInstance(val)

      Fails because java.util.List is not an instance of java.util.CollSer I think (not sure) CollSer should probably have already been replaced by a List by CollSer’s readResolve() method.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No Exception
      ACTUAL -
      Caused by: java.lang.ClassCastException: cannot assign instance of java.util.CollSer to field a.b.c.BaseClass.aField of type java.util.List in instance of a.b.c.SubClass.
          at java.base/java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2227)
          at java.base/java.io.ObjectStreamClass$FieldReflector.checkObjectFieldValueTypes(ObjectStreamClass.java:2191)
          at java.base/java.io.ObjectStreamClass.checkObjFieldValueTypes(ObjectStreamClass.java:1478)
          at java.base/java.io.ObjectInputStream$FieldValues.defaultCheckFieldValues(ObjectInputStream.java:2657)
          at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2471)
          at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2242)
          at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1742)
          at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:514)
          at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:472)
          … 4200 other lines


      CUSTOMER SUBMITTED WORKAROUND :
      Note that I have used Unmodifiable Maps Lists ... all over this code without problems. It is just à this one usage point where using List.copyOf() method causes this exception at unserialisation time. Using the original modifiable List prevents the exception from being thrown.

      FREQUENCY : rarely


      Attachments

        Issue Links

          Activity

            People

              smarks Stuart Marks
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated: