Pigsy-Monk opened a new issue, #4021:
URL: https://github.com/apache/fory/issues/4021

   ## Bug Description
   
   When a **non-collection plain class** (e.g. not `Collection`, `Map`, 
`Externalizable`, etc.) is serialized through a custom `Serializer` provided by 
a configured `SerializerFactory`, the TypeDef root kind becomes inconsistent 
between writer and reader — even when both sides run **identical code with no 
pre-registration**.
   
   **Writer** lazily resolves the class to its factory custom serializer, 
producing `TypeInfo.typeId = NAMED_EXT (32)`, which gets encoded into the 
TypeDef header byte.
   
   **Reader** decodes the same TypeDef but has no `TypeInfo` for the class yet 
(it was never serialized locally). Since the class is not in the 
`usesNonStructTypeDef` set (it's a plain class, not a Collection/Map/array), 
the reader falls back to `buildUnregisteredTypeId(cls, null)` with 
`serializer=null`, which yields `NAMED_COMPATIBLE_STRUCT (30)` under 
`CompatibleMode.COMPATIBLE` + meta share.
   
   The validation in `NativeTypeDefDecoder.isCompatibleRootKind(expected=30, 
actual=32)` fails because `NAMED_EXT` is not in the struct compatibility set.
   
   ## Error Message
   
   ```
   org.apache.fory.exception.DeserializationException: TypeDef root kind does 
not match the decoded class:
   class=com.example.MyRestriction, expected=30, actual=32, 
registeredClassLayer=false
   ```
   
   ## Reproduction
   
   Minimal repro (symmetric, same code, no pre-registration):
   
   1. Define a plain interface `Restriction` + a concrete class `MyRestriction 
implements Restriction` (**not** a Collection/Map)
   2. Define a `Container` struct with a field typed as `Restriction` 
(interface), set to `MyRestriction` at runtime
   3. Configure `SerializerFactory` to return a custom `Serializer` for 
`Restriction` subtypes
   4. Build two `Fory` instances with `CompatibleMode.COMPATIBLE` + 
`metaShare(true)` + `codegen` + the factory — **neither** pre-registers 
`MyRestriction`
   5. Serialize `Container` with instance A, deserialize with instance B → 
**DeserializationException**
   
   A full regression test is included in the fix commit: 
`TypeDefRootKindSymmetricReproTest.java`
   
   **Note**: Classes that are `Collection`/`Map`/`Externalizable` etc. do 
**not** trigger this bug because `usesNonStructTypeDef` already normalizes them 
to `NAMED_EXT`. Only **plain classes** routed through `SerializerFactory` hit 
the gap.
   
   ## Root Cause
   
   `ClassResolver.getTypeDefRootTypeId()` does not probe `SerializerFactory` 
when determining the root kind for unregistered classes. The writer has already 
resolved the factory serializer (so `TypeInfo.typeId=32`), but the reader has 
no `TypeInfo` and falls back to the struct path (`30`).
   
   ## Proposed Fix
   
   Add a `SerializerFactory` probe (with `ConcurrentHashMap` caching) in 
`ClassResolver.getTypeDefRootTypeId()`:
   
   ```java
   private final ConcurrentHashMap<Class<?>, Boolean> extSerializerFlagCache = 
new ConcurrentHashMap<>();
   
   private boolean isCustomSerializedByFactory(Class<?> cls) {
       return extSerializerFlagCache.computeIfAbsent(cls, c -> {
           try {
               return createSerializerFromFactory(c) != null;
           } catch (Throwable t) {
               return false;
           }
       });
   }
   ```
   
   Then in `getTypeDefRootTypeId`, before falling through to the struct path:
   
   ```java
   if (isCustomSerializedByFactory(cls)) {
       return Types.NAMED_EXT;
   }
   ```
   
   This is inserted in both the `hasFieldMetadata=true` branch (reader path) 
and the no-TypeInfo branch (writer path).
   
   **Performance**: `computeIfAbsent` ensures each class is probed at most 
once; subsequent calls are O(1) map lookups. `ConcurrentHashMap` provides 
thread safety without external locking.
   
   Full fix + regression test: 
https://github.com/Pigsy-Monk/fory/commit/7c70cd283
   
   ## Environment
   
   - fory-core version: confirmed on 1.5.0 (Maven Central jar) and 
1.8.0-SNAPSHOT (current main)
   - Java: 21 (Eclipse Adoptium)
   - OS: Windows 11


-- 
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