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]
