Skip to content

fix(backend): JSON-escape record property names in both backends - #313

Open
xia-chao wants to merge 1 commit into
vercel-labs:mainfrom
xia-chao:fix/json-record-key-escaping
Open

xia-chao wants to merge 1 commit into
vercel-labs:mainfrom
xia-chao:fix/json-record-key-escaping

Conversation

@xia-chao

Copy link
Copy Markdown

Summary

  • JSON-escape statically-known record property names at every key-label site in both backends. JSON.stringify built the label by interpolating the decoded name into a JSON fragment and only ran the C/LLVM string-literal escaper over it, so \ lost its escape, " closed the string and produced invalid JSON, and C0 control bytes were written verbatim.
  • The fix encodes the name with JSON.stringify at compile time and keeps the existing literal layer: cStringLiteral/host.cstr then quotes the already-valid JSON text, so the runtime buffer receives valid JSON. Index-signature keys were already correct (they call scr_jb_put_json_str); only the static-label branches were wrong.
  • Adds tests/corpus/2853-json-stringify-record-key-escapes.ts, a differential program that covers backslash, double quote, control bytes, optional-field records, index-signature records, a nested record, an array element, the space pretty printer, and a JSON.parse round-trip.

Reported in #312 (that report has the debugger screenshots for both the wrong and the correct path).

Before, on the published 0.1.1:

$ scriptc run repro.ts
{"a\nb":1,"q"q":2,"note":"ok"}
scriptc: TypeError: record has no key (typed slot — no undefined is representable)
scriptc: program killed by SIGABRT

After this change, both backends match Node v24.15.0 byte for byte:

$ scriptc run repro.ts
{"a\\nb":1,"q\"q":2,"note":"ok"}

Validation

  • node packages/cli/dist/main.js run tests/corpus/2853-json-stringify-record-key-escapes.ts (LLVM) and --backend c: stdout byte-identical to Node v24.15.0, exit 0
  • pnpm vitest run tests/harness/differential.test.ts -t "2853-json-stringify-record-key-escapes" passed
  • full tests/harness/differential.test.ts corpus run launched locally; results to follow in a comment
  • changed-file ESLint: 0 errors (only pre-existing no-non-null-assertion warnings in the touched file)
  • pnpm test:sandbox not run locally (no Vercel Sandbox credentials on this machine)

Follow-ups

The same interpolation pattern is used by the %j format path and island boundary serialization, which share this record writer, so they inherit the fix. No other raw static JSON label site remains: scr_jb_edge_prop only feeds the circular-structure error message (Node prints that name unescaped too), and the index-overflow path already used the runtime escaper.

`JSON.stringify` emitted statically-known record field names as raw bytes:
the key label was built by interpolating the decoded name into a JSON
fragment and only running the C/LLVM string-literal escaper over it. A
name containing `"` produced syntactically invalid JSON, `\` lost its
escape, and C0 control bytes were written verbatim, so the result no
longer round-tripped through `JSON.parse`.

Encode the name with `JSON.stringify` at both label sites in each
backend's record writer (all-required and droppable/overflow branches);
the existing C/LLVM literal layer then quotes the already-valid JSON
text. Index-signature keys were already correct because they go through
the runtime `scr_jb_put_json_str`.

Adds a differential corpus program covering backslash, double quote,
control bytes, optional-field records, index-signature records, nested
records, the `space` pretty printer, and a `JSON.parse` round-trip.
@vercel

vercel Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

@xia-chao is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

@xia-chao

Copy link
Copy Markdown
Author

Please review

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