ReviewOS

zig-utils/zig-js

JSON.stringify: carry stable shape slots through ordinary key snapshots

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

Problem

#1001 removed the generic [[Get]] pipeline for a current ordinary own data field, but the direct path still calls namedOwnPropertySnapshot(key) and therefore repeats a full name-to-slot shape lookup for every key that SerializeJSONObject just enumerated.

The executed symbolized JSON workload identified Shape.lookupState as the largest leaf (672 samples), ahead of generic property dispatch (307). After #1001, seven exact-parent pairs still retire about 7.03 billion instructions on each frozen 2,200 x 90 JSON row and remain far behind JavaScriptCore.

Proposed boundary

For a direct ordinary object whose key snapshot proves the simple all-data/default-attribute shape:

  • capture insertion-ordered immutable shape names together with their slots and expected Shape;
  • on each live value read, take property_lock, require the same Shape and no accessor shadow, and copy the current slot value;
  • if Shape or descriptor representation changed, fall back to the #1001 descriptor snapshot and then full [[Get]] as needed;
  • keep PropertyList, Proxy, dense-index, accessor, attribute, deletion/re-add history, module namespace, typed-array, and host-class paths on the generic key machinery.

This must not freeze values at key-enumeration time. A prior child toJSON or replacer can still update a later data value, convert it to an accessor, or delete it to expose an inherited replacement.

Completion

Add API-level snapshot/revalidation tests plus production witnesses that mutate later parent fields from an earlier child toJSON. Preserve JSON Test262, required VM/tree agreement, OOM/deep/cycle/GC and no-GIL TSan gates. Compare exact-parent alternating samples on both frozen JSON rows with exact checksums; land only if retired work measurably falls without a control regression.

Parent: #473. Depends on: #1001.

9 days ago

Landed on main in 61a7ae1e. The simple all-data/default-attribute path now carries insertion-ordered immutable Shape slots through the key snapshot and revalidates Shape plus accessor state under property_lock for every live value read. Any representation change retains the #1001 descriptor path and full [[Get]] fallback.

Seven alternating exact-parent pairs, real GC and stripped ReleaseFast:

  • representative_json (2,200 x 90): instructions 7,023,777,988 -> 6,296,184,809 (0.8964x); CPU 476.304 ms -> 408.888 ms (0.8585x); checksum 324952086 exact.
  • representative_json_variant: instructions 7,016,408,833 -> 6,287,354,986 (0.8961x); CPU 484.799 ms -> 429.079 ms (0.8851x); checksum 324952086 exact.
  • 4,096-sibling control at 50 jobs: instructions 0.7374x, CPU 0.7409x, checksum 2262150 exact.
  • depth-4,096 control at 50 jobs: instructions 0.7004x, CPU 0.7572x, checksum 3493700 exact.

Every timed boundary exceeded 50 ms except an initial depth smoke, which was discarded and rerun at 50 jobs. Thermal state stayed 0 -> 0. Qualification: API snapshot/revalidation 1/1; focused production runtime 1/1; stringify OOM/deep-cycle/GC batch 12/12; no-GIL TSan 2/2 with no report; Test262 JSON 165/165; tree/required-VM agreement 165/165. Guarded builds peaked at 2,201 MB.

Sign in to comment on this issue.