Skip to content

[JavaScript] Typed root collections ignore registered element encoding #4009

Description

@BrianLii

Search before asking

  • I had searched in the issues and found no similar issues.

Version

  • Apache Fory: 83522b197 (reproduced locally)
  • OS: macOS
  • Node.js: v20.20.2

Component(s)

JavaScript

Minimal reproduce step

From a checkout of 83522b197:

cd javascript
npm install
npm run build

node - <<'NODE'
const { default: Fory, Type } = require("./packages/core/dist");
const { BinaryReader } = require("./packages/core/dist/lib/reader");
const { TypeId } = require("./packages/core/dist/lib/type");

const fory = new Fory({ compatible: false, ref: false });
const registration = fory.register(
  Type.list(Type.int32({ encoding: "fixed" })),
);

const bytes = registration.serialize([1, 2, 3]);
const reader = new BinaryReader({});
reader.reset(bytes);
reader.readUint8(); // root bitmap
reader.readInt8(); // root flag
reader.readUint8(); // root type id for LIST

reader.readVarUint32Small7(); // list length
reader.readUint8(); // list header (= 8, same type but not declared type)
console.log({
  int32_type_id: TypeId.INT32,
  varint32_type_id: TypeId.VARINT32,
});
console.log({
  desired_type_id: TypeId.INT32,
  actual_type_id: reader.readUint8(), // element type info
});
NODE

What did you expect to see?

The registered root schema explicitly declares int32 with encoding: "fixed". I expected the serializer to use that registered type to serialize its elements instead of inferring the type.

For this example, the list header should remain 0x08 (same type, without a declared element type), followed by TypeId.INT32 (4) as inline type information. A root collection has no enclosing schema, so the output must remain self-describing.

What did you see instead?

The output is:

{ int32_type_id: 4, varint32_type_id: 5 }
{ desired_type_id: 4, actual_type_id: 5 }

The element type ID is not consistent with the fixed int32 schema. Fory infers the element type from the JavaScript runtime values instead, so integer number values use VARINT32 (5) rather than the registered INT32 (4) (see [1]).

The same declared schema is honored when the collection is a field of a registered struct, for example

Type.struct("User", { scores: Type.list(Type.int32({ encoding: "fixed" })) });

The inconsistency only occurs when the typed collection itself is registered and serialized as the root value.

The same behavior also affects Type.set(...) and Type.map(...) collections.

Anything Else?

This is a correctness issue first. The fix should also improve typed root collection serialization performance by avoiding runtime type inference and dynamic serializer selection.

[1] The registered generic schema reaches the raw list<any> serializer: Gen.generateSerializer retrieves an existing serializer by container type ID. The dynamic writer then calls getSerializerByData(item), which maps ordinary integer JavaScript number values to VARINT32, without consulting the registered generic schema.

Are you willing to submit a PR?

  • I'm willing to submit a PR!

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions