Skip to content

fix(javascript): honor declared element types for root container registration - #4013

Open
ayush00git wants to merge 3 commits into
apache:mainfrom
ayush00git:fix/root-container-generics
Open

fix(javascript): honor declared element types for root container registration#4013
ayush00git wants to merge 3 commits into
apache:mainfrom
ayush00git:fix/root-container-generics

Conversation

@ayush00git

Copy link
Copy Markdown
Contributor

What was the error

Registering a root container with declared element types, e.g. fory.register(Type.list(Type.float32())), silently returned the internal any-typed container serializer. The declared generics were discarded: elements were written with dynamic dispatch instead of the declared type, so declared float32/int64 semantics were lost at the root while the same declaration worked as a struct field.

What this PR fixes

A root Type.list/Type.set/Type.map with declared element types now gets a dedicated generated serializer, bound to the returned root serialize/deserialize pair. It is kept out of the type-id keyed registry so dynamic container dispatch stays untouched. Regression tests cover root list, set, and map plus the dynamic-dispatch guard.

…stration

Registering a root Type.list/set/map with declared element types returned
the internal any-typed container serializer, silently discarding the
declared generics (e.g. declared float32 elements kept float64 dynamic
encoding). Generate a dedicated serializer for such registrations, bind it
to the returned root deserializer, and keep it out of the type-id keyed
registry so dynamic container dispatch stays untouched.
// The type-id keyed registry only holds the dynamic container
// serializer; a container with declared element types gets a dedicated
// serializer for this registration.
return this.generate(typeInfo);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registering the container before its extension codec now leaves the generated serializer bound to undefined, even when all registration finishes before the first root operation:

class E {}
Type.ext(901)(E);

const fory = new Fory({ compatible: false });
const list = fory.register(Type.list(Type.ext(901)));
fory.register(E, { write() {}, read() {} });
list.serialize([new E()]);

traversalContainer() creates forward placeholders for structs but not extensions, so ExtSerializerGenerator.writeEmbed() captures the missing serializer in a factory-level constant. Registering E afterward cannot update that constant, and serialization fails at ext_ser.writeTypeInfo(null). Sets have the same issue. The previous dynamic root serializer resolved the codec at write time.

Please preserve registration ordering before the first operation by ensuring the generated container binds to the completed extension codec, and add a regression test for this order.

ayush00git and others added 2 commits September 3, 2026 18:06
A declared container generated before its extension codec captured
undefined, because traversalContainer only created forward placeholders
for struct types. Serialization then failed at the ext serializer's
writeTypeInfo. Register the same forward placeholder for ext types so the
generated container binds to the placeholder object that the later codec
registration fills, preserving free registration order before the first
root operation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDxaC93cfnTxCciswW1NHM
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.

2 participants