gudzpoz opened a new issue, #2251:
URL: https://github.com/apache/fury/issues/2251

   ### Search before asking
   
   - [x] I had searched in the [issues](https://github.com/apache/fury/issues) 
and found no similar issues.
   
   
   ### Version
   
   Fury: `'org.apache.fury:fury-core:0.10.2'`
   JDK: GraalVM 23.0.2+7.1 (build 23.0.2+7-jvmci-b01)
   
   ### Component(s)
   
   Java
   
   ### Minimal reproduce step
   
   A collection class with a custom serializer:
   ```java
       public static final class EmptyList extends AbstractList<Object> {
           @Override
           public Object get(int index) {
               throw new IndexOutOfBoundsException();
           }
           @Override
           public int size() {
               return 0;
           }
   
           public static final class EmptySerializer extends 
Serializer<EmptyList> {
               public EmptySerializer(Fury fury) {
                   super(fury, EmptyList.class);
               }
               @Override
               public void write(MemoryBuffer buffer, EmptyList value) {
                   // no-op
               }
               @Override
               public EmptyList read(MemoryBuffer buffer) {
                   return new EmptyList();
               }
           }
       }
   ```
   When it is put in another class:
   ```java
       public record SomeRecord(EmptyList specialList, List<Object> normalList) 
{
       }
   ```
   It will produce `ClassCastException` when serialized:
   ```java
       @Test
       public void testCollection() {
           Fury fury = Fury.builder()
                   .withLanguage(Language.JAVA)
                   .build();
           fury.register(EmptyList.class);
           fury.registerSerializer(EmptyList.class, 
EmptyList.EmptySerializer.class);
           fury.register(SomeRecord.class);
           SomeRecord someRecord = new SomeRecord(new EmptyList(), List.of());
           byte[] bytes = fury.serialize(someRecord); // <-- ClassCastException 
here
           Object output = fury.deserialize(bytes);
           SomeRecord someRecord2 = assertInstanceOf(SomeRecord.class, output);
           assertEquals(someRecord, someRecord2);
       }
   ```
   
   
   
   ### What did you expect to see?
   
   1. I expect the `testCollection` test above to pass without throwing 
exceptions.
   2. I expect being able to write a custom serializer for *any* class. Forcing 
the usage of `AbstractCollectionSerializer` for all collections is not ideal 
and there are plenty of special cases when one wants a special serializer.
   
   ### What did you see instead?
   
   ```
   2025-05-26 03:42:55 INFO  Fury:159 [Test worker] - Created new fury 
org.apache.fury.Fury@2072acb2
   2025-05-26 03:42:55 INFO  CompileUnit:55 [Test worker] - Generate code for 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest_SomeRecordFuryCodec_0 
took 31 ms.
   2025-05-26 03:42:55 INFO  JaninoUtils:121 [Test worker] - Compile 
[DumpUtilsTest_SomeRecordFuryCodec_0] take 100 ms
   
   class 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest$EmptyList$EmptySerializer
 cannot be cast to class 
org.apache.fury.serializer.collection.AbstractCollectionSerializer 
(party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest$EmptyList$EmptySerializer
 and org.apache.fury.serializer.collection.AbstractCollectionSerializer are in 
unnamed module of loader 'app')
   java.lang.ClassCastException: class 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest$EmptyList$EmptySerializer
 cannot be cast to class 
org.apache.fury.serializer.collection.AbstractCollectionSerializer 
(party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest$EmptyList$EmptySerializer
 and org.apache.fury.serializer.collection.AbstractCollectionSerializer are in 
unnamed module of loader 'app')
        at 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest_SomeRecordFuryCodec_0.<init>(DumpUtilsTest_SomeRecordFuryCodec_0.java:50)
        at 
org.apache.fury.serializer.Serializers.createSerializer(Serializers.java:129)
        at 
org.apache.fury.serializer.Serializers.newSerializer(Serializers.java:104)
        at 
org.apache.fury.resolver.ClassResolver.createSerializer(ClassResolver.java:1232)
        at 
org.apache.fury.resolver.ClassResolver.getOrUpdateClassInfo(ClassResolver.java:1170)
        at org.apache.fury.Fury.write(Fury.java:349)
        at org.apache.fury.Fury.serialize(Fury.java:273)
        at org.apache.fury.Fury.serialize(Fury.java:227)
        at 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.testCollection(DumpUtilsTest.java:95)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
   ```
   
   ### Anything Else?
   
   <details>
   <summary>The generated code</summary>
   
   The exception is from the `abstractCollectionSerializer0 = 
((AbstractCollectionSerializer)classResolver.getRawSerializer(party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest$EmptyList.class));`
 line.
   
   ```java
   package party.iroiro.juicemacs.elisp.runtime.pdump;
   
   import org.apache.fury.Fury;
   import org.apache.fury.memory.MemoryBuffer;
   import org.apache.fury.resolver.NoRefResolver;
   import org.apache.fury.memory.Platform;
   import org.apache.fury.resolver.ClassInfo;
   import org.apache.fury.resolver.ClassInfoHolder;
   import org.apache.fury.resolver.ClassResolver;
   import org.apache.fury.builder.Generated;
   import org.apache.fury.serializer.CodegenSerializer.LazyInitBeanSerializer;
   import org.apache.fury.serializer.EnumSerializer;
   import org.apache.fury.serializer.Serializer;
   import org.apache.fury.serializer.StringSerializer;
   import org.apache.fury.serializer.ObjectSerializer;
   import org.apache.fury.serializer.CompatibleSerializer;
   import org.apache.fury.serializer.collection.AbstractCollectionSerializer;
   import org.apache.fury.serializer.collection.AbstractMapSerializer;
   import org.apache.fury.builder.Generated.GeneratedObjectSerializer;
   
   public final class DumpUtilsTest_SomeRecordFuryCodec_0 extends 
GeneratedObjectSerializer {
   
     private final NoRefResolver refResolver;
     private final ClassResolver classResolver;
     private final StringSerializer strSerializer;
     private Fury fury;
     private ClassInfo listClassInfo;
     private final ClassInfoHolder objectClassInfoHolder;
     private final org.apache.fury.serializer.Serializers.EmptyObjectSerializer 
emptyObjectSerializer;
     private ClassInfo object1ClassInfo;
     private final AbstractCollectionSerializer abstractCollectionSerializer0;
     private final ClassInfoHolder object5ClassInfoHolder;
     private ClassInfo object7ClassInfo;
     private final ClassInfoHolder list5ClassInfoHolder;
     private final ClassInfoHolder object11ClassInfoHolder;
   
     public DumpUtilsTest_SomeRecordFuryCodec_0(Fury fury, Class classType) {
         super(fury, classType);
         this.fury = fury;
         fury.getClassResolver().setSerializerIfAbsent(classType, this);
     
         org.apache.fury.resolver.RefResolver refResolver0 = 
fury.getRefResolver();
         refResolver = ((NoRefResolver)refResolver0);
         classResolver = fury.getClassResolver();
         strSerializer = fury.getStringSerializer();
         listClassInfo = classResolver.nilClassInfo();
         objectClassInfoHolder = classResolver.nilClassInfoHolder();
         emptyObjectSerializer = 
((org.apache.fury.serializer.Serializers.EmptyObjectSerializer)classResolver.getRawSerializer(java.lang.Object.class));
         object1ClassInfo = classResolver.nilClassInfo();
         abstractCollectionSerializer0 = 
((AbstractCollectionSerializer)classResolver.getRawSerializer(party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest$EmptyList.class));
         object5ClassInfoHolder = classResolver.nilClassInfoHolder();
         object7ClassInfo = classResolver.nilClassInfo();
         list5ClassInfoHolder = classResolver.nilClassInfoHolder();
         object11ClassInfoHolder = classResolver.nilClassInfoHolder();
     }
   
     private AbstractCollectionSerializer writeCollectionClassInfo(MemoryBuffer 
memoryBuffer, java.util.List list1) {
         ClassResolver classResolver = this.classResolver;
         Class value = listClassInfo.getCls();
         Class cls = list1.getClass();
         if ((value != cls)) {
             listClassInfo = classResolver.getClassInfo(cls);
         }
         classResolver.writeClass(memoryBuffer, listClassInfo);
         return ((AbstractCollectionSerializer)listClassInfo.getSerializer());
     }
   
     private void sameElementClassWrite(java.util.List list2, boolean value0, 
int value1, MemoryBuffer memoryBuffer1, int value2) {
         Serializer serializer;
         if (((value1 & 4) != 4)) {
             serializer = emptyObjectSerializer;
         } else {
             serializer = objectClassInfoHolder.getSerializer();
         }
         for (int i = 0; i < value2; i+=1) {
           Object object0 = list2.get(i);
           if (value0) {
               if ((object0 == null)) {
                   memoryBuffer1.writeByte(((byte)-3));
               } else {
                   memoryBuffer1.writeByte(((byte)-1));
                   serializer.write(memoryBuffer1, object0);
               }
           } else {
               serializer.write(memoryBuffer1, object0);
           }
         }
     }
   
     private void writeClassAndObject(MemoryBuffer memoryBuffer2, Object 
object2) {
         ClassResolver classResolver = this.classResolver;
         Class value3 = object1ClassInfo.getCls();
         Class cls0 = object2.getClass();
         if ((value3 != cls0)) {
             object1ClassInfo = classResolver.getClassInfo(cls0);
         }
         classResolver.writeClass(memoryBuffer2, object1ClassInfo);
         object1ClassInfo.getSerializer().write(memoryBuffer2, object2);
     }
   
     private void writeClassAndObject1(MemoryBuffer memoryBuffer3, Object 
object3) {
         ClassResolver classResolver = this.classResolver;
         Class value4 = object1ClassInfo.getCls();
         Class cls1 = object3.getClass();
         if ((value4 != cls1)) {
             object1ClassInfo = classResolver.getClassInfo(cls1);
         }
         classResolver.writeClass(memoryBuffer3, object1ClassInfo);
         object1ClassInfo.getSerializer().write(memoryBuffer3, object3);
     }
   
     private void writeFields(MemoryBuffer memoryBuffer4, 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.SomeRecord 
someRecord1) {
         java.util.List normalList = someRecord1.normalList();
         if ((normalList == null)) {
             memoryBuffer4.writeByte(((byte)-3));
         } else {
             memoryBuffer4.writeByte(((byte)-1));
             AbstractCollectionSerializer abstractCollectionSerializer = 
this.writeCollectionClassInfo(memoryBuffer4, normalList);
             if (abstractCollectionSerializer.supportCodegenHook()) {
                 java.util.List list0 = 
(java.util.List)abstractCollectionSerializer.onCollectionWrite(memoryBuffer4, 
normalList);
                 int value5 = list0.size();
                 if ((value5 > 0)) {
                     int value6 = 
abstractCollectionSerializer.writeTypeNullabilityHeader(memoryBuffer4, list0, 
java.lang.Object.class, objectClassInfoHolder);
                     boolean sameElementClass = (value6 & 8) != 8;
                     boolean hasNull = (value6 & 2) == 2;
                     if (sameElementClass) {
                         this.sameElementClassWrite(list0, hasNull, value6, 
memoryBuffer4, value5);
                     } else {
                         for (int i0 = 0; i0 < value5; i0+=1) {
                           Object object4 = list0.get(i0);
                           if (hasNull) {
                               if ((object4 == null)) {
                                   memoryBuffer4.writeByte(((byte)-3));
                               } else {
                                   memoryBuffer4.writeByte(((byte)-1));
                                   this.writeClassAndObject(memoryBuffer4, 
object4);
                               }
                           } else {
                               this.writeClassAndObject1(memoryBuffer4, 
object4);
                           }
                         }
                     }
                 }
             } else {
                 abstractCollectionSerializer.write(memoryBuffer4, normalList);
             }
         }
     }
   
     private void sameElementClassWrite1(int value7, java.util.List list3, int 
value8, boolean value9, MemoryBuffer memoryBuffer5) {
         Serializer serializer0;
         if (((value7 & 4) != 4)) {
             serializer0 = emptyObjectSerializer;
         } else {
             serializer0 = object5ClassInfoHolder.getSerializer();
         }
         for (int i1 = 0; i1 < value8; i1+=1) {
           Object object6 = list3.get(i1);
           if (value9) {
               if ((object6 == null)) {
                   memoryBuffer5.writeByte(((byte)-3));
               } else {
                   memoryBuffer5.writeByte(((byte)-1));
                   serializer0.write(memoryBuffer5, object6);
               }
           } else {
               serializer0.write(memoryBuffer5, object6);
           }
         }
     }
   
     private void writeClassAndObject2(MemoryBuffer memoryBuffer6, Object 
object8) {
         ClassResolver classResolver = this.classResolver;
         Class value10 = object7ClassInfo.getCls();
         Class cls2 = object8.getClass();
         if ((value10 != cls2)) {
             object7ClassInfo = classResolver.getClassInfo(cls2);
         }
         classResolver.writeClass(memoryBuffer6, object7ClassInfo);
         object7ClassInfo.getSerializer().write(memoryBuffer6, object8);
     }
   
     private void writeClassAndObject3(MemoryBuffer memoryBuffer7, Object 
object9) {
         ClassResolver classResolver = this.classResolver;
         Class value11 = object7ClassInfo.getCls();
         Class cls3 = object9.getClass();
         if ((value11 != cls3)) {
             object7ClassInfo = classResolver.getClassInfo(cls3);
         }
         classResolver.writeClass(memoryBuffer7, object7ClassInfo);
         object7ClassInfo.getSerializer().write(memoryBuffer7, object9);
     }
   
     private void writeFields1(MemoryBuffer memoryBuffer8, 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.SomeRecord 
someRecord2) {
         AbstractCollectionSerializer abstractCollectionSerializer0 = 
this.abstractCollectionSerializer0;
         party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.EmptyList 
specialList = someRecord2.specialList();
         if ((specialList == null)) {
             memoryBuffer8.writeByte(((byte)-3));
         } else {
             memoryBuffer8.writeByte(((byte)-1));
             if (abstractCollectionSerializer0.supportCodegenHook()) {
                 java.util.List list4 = 
(java.util.List)abstractCollectionSerializer0.onCollectionWrite(memoryBuffer8, 
specialList);
                 int value12 = list4.size();
                 if ((value12 > 0)) {
                     int value13 = 
abstractCollectionSerializer0.writeTypeNullabilityHeader(memoryBuffer8, list4, 
java.lang.Object.class, object5ClassInfoHolder);
                     boolean sameElementClass0 = (value13 & 8) != 8;
                     boolean hasNull0 = (value13 & 2) == 2;
                     if (sameElementClass0) {
                         this.sameElementClassWrite1(value13, list4, value12, 
hasNull0, memoryBuffer8);
                     } else {
                         for (int i2 = 0; i2 < value12; i2+=1) {
                           Object object10 = list4.get(i2);
                           if (hasNull0) {
                               if ((object10 == null)) {
                                   memoryBuffer8.writeByte(((byte)-3));
                               } else {
                                   memoryBuffer8.writeByte(((byte)-1));
                                   this.writeClassAndObject2(memoryBuffer8, 
object10);
                               }
                           } else {
                               this.writeClassAndObject3(memoryBuffer8, 
object10);
                           }
                         }
                     }
                 }
             } else {
                 abstractCollectionSerializer0.write(memoryBuffer8, 
specialList);
             }
         }
     }
   
     private void differentTypeElemsRead(int value14, boolean value15, 
java.util.Collection collection, MemoryBuffer memoryBuffer9) {
         ClassResolver classResolver = this.classResolver;
         for (int i3 = 0; i3 < value14; i3+=1) {
           if (value15) {
               if ((memoryBuffer9.readByte() != ((byte)-3))) {
                   collection.add(classResolver.readClassInfo(memoryBuffer9, 
object11ClassInfoHolder).getSerializer().read(memoryBuffer9));
               } else {
                   collection.add(null);
               }
           } else {
               collection.add(classResolver.readClassInfo(memoryBuffer9, 
object11ClassInfoHolder).getSerializer().read(memoryBuffer9));
           }
         }
     }
   
     private void differentTypeElemsRead1(boolean value16, java.util.Collection 
collection1, int value17, MemoryBuffer memoryBuffer10) {
         ClassResolver classResolver = this.classResolver;
         for (int i4 = 0; i4 < value17; i4+=1) {
           if (value16) {
               if ((memoryBuffer10.readByte() != ((byte)-3))) {
                   collection1.add(classResolver.readClassInfo(memoryBuffer10, 
object11ClassInfoHolder).getSerializer().read(memoryBuffer10));
               } else {
                   collection1.add(null);
               }
           } else {
               collection1.add(classResolver.readClassInfo(memoryBuffer10, 
object11ClassInfoHolder).getSerializer().read(memoryBuffer10));
           }
         }
     }
   
     @Override public final void write(MemoryBuffer buffer, Object obj) {
         party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.SomeRecord 
someRecord3 = 
(party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.SomeRecord)obj;
         this.writeFields(buffer, someRecord3);
         this.writeFields1(buffer, someRecord3);
     }
   
     @Override public final Object read(MemoryBuffer buffer) {
         ClassResolver classResolver = this.classResolver;
         AbstractCollectionSerializer abstractCollectionSerializer0 = 
this.abstractCollectionSerializer0;
         Object object15;
         if ((buffer.readByte() != ((byte)-3))) {
             AbstractCollectionSerializer collectionSerializer = 
(AbstractCollectionSerializer)classResolver.readClassInfo(buffer, 
list5ClassInfoHolder).getSerializer();
             Object object14;
             if (collectionSerializer.supportCodegenHook()) {
                 java.util.Collection collection2 = 
collectionSerializer.newCollection(buffer);
                 int size = collectionSerializer.getAndClearNumElements();
                 if ((size > 0)) {
                     int flags = buffer.readByte();
                     boolean sameElementClass1 = (flags & 8) != 8;
                     boolean hasNull1 = (flags & 2) == 2;
                     if (sameElementClass1) {
                         Serializer serializer1;
                         if (((flags & 4) != 4)) {
                             serializer1 = emptyObjectSerializer;
                         } else {
                             serializer1 = classResolver.readClassInfo(buffer, 
object11ClassInfoHolder).getSerializer();
                         }
                         for (int i5 = 0; i5 < size; i5+=1) {
                           if (hasNull1) {
                               if ((buffer.readByte() != ((byte)-3))) {
                                   collection2.add(serializer1.read(buffer));
                               } else {
                                   collection2.add(null);
                               }
                           } else {
                               collection2.add(serializer1.read(buffer));
                           }
                         }
                     } else {
                         this.differentTypeElemsRead(size, hasNull1, 
collection2, buffer);
                     }
                 }
                 Object object12 = 
collectionSerializer.onCollectionRead(collection2);
                 object14 = object12;
             } else {
                 Object object13 = collectionSerializer.read(buffer);
                 object14 = object13;
             }
             object15 = object14;
         } else {
             object15 = null;
         }
         
         java.util.List castedValue = (java.util.List)object15;
         Object object19;
         if ((buffer.readByte() != ((byte)-3))) {
             Object object18;
             if (abstractCollectionSerializer0.supportCodegenHook()) {
                 java.util.Collection collection3 = 
abstractCollectionSerializer0.newCollection(buffer);
                 int size1 = 
abstractCollectionSerializer0.getAndClearNumElements();
                 if ((size1 > 0)) {
                     int flags1 = buffer.readByte();
                     boolean sameElementClass2 = (flags1 & 8) != 8;
                     boolean hasNull2 = (flags1 & 2) == 2;
                     if (sameElementClass2) {
                         Serializer serializer15;
                         if (((flags1 & 4) != 4)) {
                             serializer15 = emptyObjectSerializer;
                         } else {
                             serializer15 = classResolver.readClassInfo(buffer, 
object11ClassInfoHolder).getSerializer();
                         }
                         for (int i6 = 0; i6 < size1; i6+=1) {
                           if (hasNull2) {
                               if ((buffer.readByte() != ((byte)-3))) {
                                   collection3.add(serializer15.read(buffer));
                               } else {
                                   collection3.add(null);
                               }
                           } else {
                               collection3.add(serializer15.read(buffer));
                           }
                         }
                     } else {
                         this.differentTypeElemsRead1(hasNull2, collection3, 
size1, buffer);
                     }
                 }
                 Object object16 = 
abstractCollectionSerializer0.onCollectionRead(collection3);
                 object18 = object16;
             } else {
                 Object object17 = abstractCollectionSerializer0.read(buffer);
                 object18 = object17;
             }
             object19 = object18;
         } else {
             object19 = null;
         }
         
         party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.EmptyList 
castedValue1 = 
(party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.EmptyList)object19;
         party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.SomeRecord 
someRecord4 = new 
party.iroiro.juicemacs.elisp.runtime.pdump.DumpUtilsTest.SomeRecord(castedValue1,
 castedValue);
         return someRecord4;
     }
   
   }
   ```
   
   </details>
   
   ### Are you willing to submit a PR?
   
   - [ ] I'm willing to submit a PR!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to