Skip to content

Resolve parser aliases at definition time to avoid collisions - #327

Open
keichi wants to merge 1 commit into
masterfrom
fix-alias-registry-collision
Open

keichi wants to merge 1 commit into
masterfrom
fix-alias-registry-collision

Conversation

@keichi

@keichi keichi commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Problem

namely(alias) stores parsers in a single module-global aliasRegistry, and a reference by name (nest/array/choice/pointer/wrapped with type: "name") was only looked up again at compile time, which happens lazily on the first parse(). If two independent modules (or two libraries that both depend on binary-parser) used the same alias name, the parser registered last silently replaced the other one, so already-defined parsers would parse with the wrong layout. Generated function names were also derived from the alias (___parser_<alias>), so two different parsers with the same alias in one compilation produced colliding functions.

Repro

Parser.start().namely("x").uint8("a");
const first = Parser.start().nest("v", { type: "x" });

Parser.start().namely("x").uint16le("b"); // e.g. in another module
first.parse(Buffer.from([1, 2])); // { v: { b: 513 } } instead of { v: { a: 1 } }

Fix

  • When a field is defined, string types that name a registered alias (type, choices, defaultChoice) are resolved and the Parser object is captured on that field. Code generation uses the captured parser, and falls back to the registry only for names that were not registered at definition time (wrapped does not validate its type, so this keeps its old behavior).
  • Each parser gets a unique numeric id. Generated functions are named ___parser_<id> and compile-time references are tracked by Parser object instead of by alias name.
  • Self-recursion and forward references keep working: array/nest/choice/pointer already required the alias to be registered when the field is defined, so the parser can always be captured. A self-reference resolves to the same parser object, which is marked resolved as before.

The public API does not change. Re-registering an alias now only affects fields defined after that point (documented in the README under namely). Side effect: alias strings no longer appear in generated code, since function names come from the id.

Added a regression test in test/composite_parser.ts. It fails on master and passes with this change. All existing tests pass, and the classfile/elf32/tar/mbr examples still run.

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.

1 participant