Skip to content

Serialize an eligible class instance's JSON from its layout - #3555

Open
ctate wants to merge 6 commits into
mainfrom
ctate/serialize-eligible-class-f0052e
Open

ctate wants to merge 6 commits into
mainfrom
ctate/serialize-eligible-class-f0052e

Conversation

@ctate

@ctate ctate commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator
  • Decide in the frontend when a class instance's own properties are exactly its layout fields, in creation order, with no toJSON or subclass instance that could reach the value.
  • Write an eligible instance's fields from their slots through the existing type-directed writers, and keep the runtime walk for an instance with properties past the layout, for cycles, and for every ineligible class.
  • Add a differential corpus program covering the field types, inheritance, nullable references, dropped values, cycles, and the instances that keep the runtime walk.

@vercel

vercel Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scriptc Ready Ready Preview, v0 Nov 13, 2026 1:29am UTC

@vercel-internal-security-reviewer

Copy link
Copy Markdown

Security review details

Comment on lines +76 to +77
if (field.type.kind === "func") continue;
if (field.name === "toJSON") return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A function-valued toJSON field is skipped before the hook check, so eligible instances serialize their fields instead of invoking toJSON.

Fix on Vercel

Comment on lines +93 to +101
function ownOrderMatchesLayout(info: ClassInfo): boolean {
const declared = info.def.fields.slice(info.base?.def.fields.length ?? 0);
const own = declared.map((field) => field.name).filter(isClassOwnEnumerableFieldName);
const created = info.fieldOrder
.filter((field) => field.redeclared !== true)
.map((field) => field.name)
.filter(isClassOwnEnumerableFieldName);
return own.length === created.length && own.every((name, index) => name === created[index]);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Classes with array-index field names pass the layout-order check but emit JSON keys in declaration order instead of JavaScript's numeric-key order.

Fix on Vercel

Comment on lines +179 to +185
buf: string,
obj: string,
className: string,
): void {
const conv = host.dyn.toDynHelper({ kind: "object", className });
const box = B.tmp();
B.line(`${box} = call ptr @${conv}(ptr ${obj}) ; +1`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When an untyped reference adds toJSON to an otherwise eligible instance, the property-bag fallback serializes its fields without invoking the hook.

Fix on Vercel

This branch was successfully deployed

1 active deployment
Preview — d5775996 Deployed Nov 13, 2026 by vercel[bot]
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