Skip to content

fix(core): keep TypeDef root kind consistent for factory custom serializers - #4022

Open
Pigsy-Monk wants to merge 5 commits into
apache:mainfrom
Pigsy-Monk:main_0830
Open

fix(core): keep TypeDef root kind consistent for factory custom serializers#4022
Pigsy-Monk wants to merge 5 commits into
apache:mainfrom
Pigsy-Monk:main_0830

Conversation

@Pigsy-Monk

@Pigsy-Monk Pigsy-Monk commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #4021.

A fresh reader can infer a struct kind for a plain class whose configured
SerializerFactory actually supplies an extension serializer. Native TypeDef
validation then rejects bytes written by an identically configured instance.

Resolve empty extension roots through the existing ClassResolver.getTypeInfo
path before validating their kind. The resolver retains the actual serializer
and type ID, including when metadata previously created a provisional TypeInfo.
This requires no additional cache or factory probe.

Keep each serializer's root definition on TypeInfo, independently of the class
field schemas used by metadata probes and ObjectStream layers. Local-header
matching checks an available root definition before the field schema, without
creating a serializer just to probe for a match. This preserves cold registered
struct and ObjectStream layer matches and prevents a cached field schema from
replacing an extension's empty root definition.

Tests

Expanded SerializerFactoryTest covers the original polymorphic custom serializer,
metadata-first and typed-root reads, read-then-write reuse, enum and recursive
struct factories, factory failure recovery, registration, and both codegen modes.
It also verifies that each custom serializer is created once per resolver.

  • Full fory-core suite on JDK 21: 2,252 passed, 58 skipped, no failures or errors.
  • Includes cold native ID/name registration and typed/dynamic reads, verifying
    that exact-local schemas do not consume the remote-schema allowance, plus
    existing ObjectStream layer, registration, and schema-consistent meta-share cases.
  • Local cross-language peer tests were not run; the change preserves the native wire format.
  • Spotless and Checkstyle passed.

…lizers

ClassResolver.getTypeDefRootTypeId treated unregistered classes handled by a configured SerializerFactory as struct-owned (NAMED_COMPATIBLE_STRUCT=30) when no TypeInfo existed yet, while the writer encoded NAMED_EXT=32. Probe the SerializerFactory (cached, re-entrancy guarded, conservative on error): when it would supply a custom serializer, normalize the root kind to NAMED_EXT so reader and writer agree.

Regression test: two identical instances sharing meta contexts, neither pre-registering the factory-serialized class, round-trip a struct that nests the non-collection subclass; the pre-registration variant is also verified.
Pigsy-Monk and others added 4 commits September 5, 2026 16:40
…omputeIfAbsent

Replace three HashSet caches + manual re-entrancy guard with a single
ConcurrentHashMap<Class<?>, Boolean> and computeIfAbsent. Thread-safe,
simpler, same semantics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeDef root kind mismatch (30 vs 32) for non-collection classes with SerializerFactory custom serializer

2 participants