ReviewOS

zig-utils/zig-js

JSON.stringify: format array index keys without arena allocation

#1003
Closed glennmichael123 opened this 9 days ago ·
9 days ago

Problem

Stringifier.serializeArray currently calls std.fmt.allocPrint(self.arena, "{d}", .{i}) for every serialized index. The key is consumed synchronously by [[Get]]; when toJSON or a replacer must observe it, serializeLoaded already materializes its own JavaScript String value before the callback.

Each frozen 2,200 x 90 representative JSON row stringifies one three-element values array per round: 198,000 array serializations and exactly 594,000 arena-backed decimal-key allocations. Those bytes stay in Context backing rather than following the invocation-scoped snapshot/output lifetimes fixed in #993/#996/#997.

Change boundary

Format each usize index into bounded stack storage and pass that borrowed slice through the synchronous SerializeJSONProperty call. Preserve exact decimal spelling for toJSON/replacer callbacks, sparse holes, inherited indices, accessors, proxies, abrupt completion, and deep/OOM behavior. This change does not authorize direct dense-element reads; that is a separate descriptor/locking decision.

Completion

Add callback/key and sparse/accessor witnesses, retain JSON Test262 and required VM/tree agreement, and rerun OOM/deep/cycle/GC plus no-GIL TSan gates. Compare exact-parent alternating samples on both frozen JSON rows and an array-heavy stringify control with exact checksums. Land only on a measured non-regression.

Parent: #473. Follows #1001 and #1002.

9 days ago

Landed on main as c79e5d0. The array serializer now formats decimal index keys in a bounded stack buffer; callback-visible keys are still materialized as ordinary JavaScript Strings before user code runs. Seven exact-parent alternating pairs retained exact checksums: representative_json 0.9902x retired instructions / 0.9985x CPU, representative_json_variant 0.9890x / 0.9609x, and the array-heavy shallow-4096 control 0.9428x / 0.9658x. The object-only depth-4096 control stayed neutral at 1.0022x / 0.9993x. Gates: focused witness 1/1, JSON stringify semantic/OOM/deep/GC 13/13, TSan/no-GIL 2/2, JSON Test262 165/165, required VM/tree agreement 165/165. The backing-allocation telemetry was not used as a win claim because arena bump allocation obscures the logical per-key calls and its single-sample growth count was noisy.

Sign in to comment on this issue.