ReviewOS

zig-utils/zig-js

ABI: retain and export structured JSC exception stack frames

#249
Closed chris opened this 9 days ago ·
9 days ago

Goal

Implement JSC__Exception__getStackTrace by retaining structured exception frames in zig-js and populating the pinned Home/Bun ZigStackTrace / ZigStackFrame ABI exactly.

Parent tracking: #134, #140, #163, #164.

Why this is a runtime slice

The current engine exposes formatted Error.prototype.stack text, while upstream reads Exception::stack() and calls populateStackTrace(..., OnlyPosition). Parsing formatted text would lose frame kind, async state, exact function/source ownership, JSC frame indexes, and zero-based positions. This issue therefore owns the structured representation rather than a compatibility stub.

Required implementation

  • Add a precise-GC-safe structured frame vector captured at throw/exception creation across tree-walker and VM-trampoline execution.
  • Preserve function name, source URL, zero-based line/column, line-start byte, code kind, async/constructor state, and stable frame ordering.
  • Retain structured frames through rethrow, Promise rejection, module evaluation, sibling realms, and exception-cell publication.
  • Implement the exact ZigStackTrace and ZigStackFrame extern layouts, capacities, truncation, caller-owned output, and BunString ownership rules.
  • Leave source-line collection to the separate ZigException__collectSourceLines surface while matching upstream OnlyPosition behavior here.
  • Cover empty/truncated traces, syntax/runtime errors, recursion, async frames, foreign VM rejection, pending-exception preservation, and precise collection.
  • Promote both pinned inventories and update README/API/Home integration evidence.

Evidence gate

  • Home and Bun private audits have zero unclassified declarations.
  • Compiled Home consumer verifies byte offsets and populated frames.
  • Targeted stack/error/module/Promise/GC batch passes with zero leaks.
  • Full suite is batched with adjacent slices rather than rerun for every edit.
  • Benchmark artifact remains current or is regenerated if measured code changes.
9 days ago

Implementation is now on main in two focused commits:

  • da8cc441 — retain creation-time structured Error/DOMException frames across tree-walker, VM, generators/async, constructors, globals, and modules; export exact JSC__Exception__getStackTrace layouts/ownership.
  • 01fcf42c — publish the consumer contract and update Home/Bun inventories and README/API/integration docs.

Focused evidence is green:

  • Home private inventory: 250/431 implemented, 181 pending, zero unclassified.
  • Bun core inventory: 244/421 implemented, 177 pending, zero unclassified.
  • zig build test-home-private-abi -Doptimize=Debug: 249/249 linked and runtime matrix passed.
  • Runtime fixture covers nested function/global frames, line-41 source mapping to zero-based line 40, owned source URL/name strings, caller capacity truncation, same-VM sibling realms, and foreign-VM rejection.

A clean full benchmark comparison is currently running because always-on location retention changes runtime bookkeeping. I will attach the preserved 1,540-sample report and final broad validation before closing this issue.

9 days ago

Implemented and published on main in four intentionally small commits:

  • da8c653b — retain structured exception frames and implement the exact ABI
  • 01fcf42c — publish API/Home/inventory evidence
  • 05c3751d — keep retained stacks off the fixed cold sidecar
  • 2b0f1948 — publish the refreshed 1,540-sample JSC comparison

Final evidence:

  • Home private ABI audit: 250/431 implemented, 181 pending, zero unclassified.
  • Bun core audit: 244/421 implemented, 177 pending, zero unclassified.
  • Compiled Home consumer: 249/249 symbols linked; runtime matrix passed.
  • Stack coverage includes nested/global frames, exact zero-based positions, owned source URLs, caller-cap truncation, same-VM sibling realms, foreign-VM rejection, async/constructor metadata, and precise collection.
  • Batched unit run reached 877/878; its sole failure identified the cold-sidecar growth. The compact pointer representation in 05c3751d restores the 128-byte slab / 224-byte cold-state invariant, and targeted slab plus ABI tests pass afterward. No other broad-suite test failed or leaked.
  • Benchmark publication validators pass. The current report records all 1,540 samples: zig-js wins 10/10 direct rows at 2.43x geometric-mean throughput, 10/10 eight-lane warmed rows at 2.71x, and 9/10 eight-lane cold rows at 2.53x. Shared-realm eight-lane scaling is 3.84x; object churn remains the documented contention target.

Sign in to comment on this issue.