ReviewOS

zig-utils/zig-js

JSON.stringify: snapshot enumerability before property serialization

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

Parent: #473

SerializeJSONObject must form its key list from enumerable own string keys before it serializes any property value. Current zig-js snapshots all own keys, then rechecks each descriptor inside the serialization loop. A getter for an earlier property can therefore change whether a later captured key is emitted, and Proxy getOwnPropertyDescriptor traps are interleaved with value get traps instead of completing during key-list construction.

Executed reproduction:

var object = {};
Object.defineProperty(object, "first", {
  enumerable: true,
  get() {
    Object.defineProperty(object, "later", { enumerable: false });
    return 1;
  },
});
Object.defineProperty(object, "later", {
  value: 3, writable: true, enumerable: true, configurable: true,
});
JSON.stringify(object);
  • Node/spec oracle: {"first":1,"later":3}
  • current zig-js: {"first":1}

The adjacent deletion/inherited-value case was also executed and already matches Node: deleting a captured own key may expose and serialize an inherited replacement during the later Get. The fix must preserve that behavior; it must snapshot only key eligibility, not values.

Definition of done:

  • ordinary getter mutation and Proxy trap-order tests
  • enumerability filtered before any SerializeJSONProperty call
  • inherited replacement remains observable after deletion
  • replacer PropertyList semantics unchanged (it does not filter by enumerability)
  • focused JSON, Test262 JSON, VM agreement, OOM/GC/no-GIL/TSan clean
9 days ago

Completed on main in 4ccb11d2.

Evidence:

  • executed Node differential now matches: {"first":1,"later":3}
  • captured-key deletion still observes inherited replacement: {"first":1,"later":2}
  • Proxy test pins all descriptor traps before value gets
  • replacer PropertyList still includes a non-enumerable own property
  • focused JSON: 28 passed
  • Test262 JSON: 165 passed, 0 failed
  • VM agreement: 165 agree, 0 diverge
  • no-GIL TSan: 2 passed
  • frozen base/variant checksums and all allocation/lock/shape/GC counters unchanged
  • five-sample process-CPU medians neutral-to-better on both frozen rows

Sign in to comment on this issue.