typat opened a new issue, #3782:
URL: https://github.com/apache/fory/issues/3782

   ### Search before asking
   
   - [x] I had searched in the [issues](https://github.com/apache/fory/issues) 
and found no similar issues.
   
   
   ### Version
   
   fory-core 1.2.0; JDK 25 / 26 (reproduced on Zulu 26.28+63); 
withCompatible(true), requireClassRegistration(false), 
withAsyncCompilation(true), withCodegen(true) (default); Fory and the domain 
types share one classpath/classloader.
   
   ### Component(s)
   
   Java
   
   ### Minimal reproduce step
   
   a record `Meta(Link link, Tag tag, Set<Flag> flags, Set<Mode> modes, String 
label)` of nested records, registered on a `buildThreadSafeForyPool` with the 
config above, `serialize(...)` then `Thread.sleep` to let the compiler threads 
surface the exception. (I have a ~50-line standalone ForyRepro.java I can 
attach.)
   
   ### What did you expect to see?
   
   Serializing a registered composite type (a record with nested registered 
records) JIT-compiles its codec in the background, fully installs it (nested 
serializers injected into the codec's serializer/serializer1 fields) and 
produces no exceptions. The fory-jit-compiler-* threads stay alive and the 
generated codec is used on subsequent calls. I.e. identical behavior to JDK ≤ 
24.
   
   ### What did you see instead?
   
   serialize(...) returns correct bytes (interpreter fallback), but the 
background compiler threads throw:
   
   ```
   Exception in thread "fory-jit-compiler-2" 
java.lang.UnsupportedOperationException:
      can't get field offset on a hidden class:
      org.apache.fory.serializer.Serializer 
<pkg>.<Type>ForyCodec_0/0x….serializer
        at sun.misc.Unsafe.objectFieldOffset(...)
        at 
org.apache.fory.reflect.InstanceFieldAccessors$InstanceAccessor.fieldOffset(InstanceFieldAccessors.java:136)
        at 
org.apache.fory.reflect.InstanceFieldAccessors.createAccessor(InstanceFieldAccessors.java:68)
        at 
org.apache.fory.reflect.ReflectionUtils.setObjectFieldValue(ReflectionUtils.java:471)
        at 
org.apache.fory.builder.Generated$GeneratedSerializer$1.onNotifyResult(Generated.java:85)
        at 
org.apache.fory.builder.JITContext.lambda$registerSerializerJITCallback$0(JITContext.java:96)
    (repeated for .serializer1), immediately followed by:
    java.lang.NullPointerException: Cannot invoke "java.util.List.iterator()" 
because the return value of
      "java.util.Map.get(Object)" is null
        at 
org.apache.fory.builder.JITContext.lambda$registerSerializerJITCallback$0(JITContext.java:95)
   ```
   
   The fory-jit-compiler-* threads die, and the generated codec is left with 
its nested-serializer fields never injected. Only composite types fail; 
leaf-type codecs (no nested serializer field) compile cleanly even though they 
too are hidden classes.
   
   ### Anything Else?
   
   I tried `--add-opens=java.base/java.lang.invoke=ALL-UNNAMED` does not help 
(verified). Fory's JDK25+ docs recommend this flag, so I tested with and 
without it on Zulu 26.28+63. The exception is identical in both cases. That 
flag governs `MethodHandles.Lookup/defineHiddenClass` access; it has no effect 
on `Unsafe.objectFieldOffset`, which the JVM rejects for any hidden declaring 
class regardless of module openness. The failing accessor 
(InstanceFieldAccessors$InstanceAccessor) never consults a Lookup (tt calls 
Unsafe.objectFieldOffset directly) so this is a code-path defect, not a 
missing-access/configuration issue.
   
   On JDK 25+, `CodecUtils.codecNeighbor` returns the bean class for any type 
whose loader can see Fory, so `CodeGenerator.compileAndLoad` defines the codec 
via `DefineClass.defineHiddenNestmate` (a hidden class). When 
`withAsyncCompilation(true)`, nested serializers are injected post-construction 
through `Generated$GeneratedSerializer$1.onNotifyResult` → 
`ReflectionUtils.setObjectFieldValue` → `InstanceFieldAccessors.createAccessor` 
→ `new InstanceAccessor` → `Unsafe.objectFieldOffset(field)` 
`(InstanceFieldAccessors.java:136)`, which the JVM rejects for hidden classes. 
The failing task's finally then runs `hasJITResult.clear()` while a concurrent 
successful task is at `JITContext.java:95` `(for (… : 
hasJITResult.get(callback.id())))`, causing the NPE. Only composite types are 
affected (leaf codecs have no nested serializer field to inject).
   
   Workaround: 
   `withAsyncCompilation(false)` (keeps codegen/JIT) or `withCodegen(false)` 
(interpreter only).
   
   
   
   ### 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