ReviewOS

zig-utils/zig-js

ABI: preserve debugger async-call lifecycle and stacks

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

Parent: #140 Tracked by: #134, #139, #163, #164

Implement the four shared Home/Bun Debugger__*AsyncCall exports as one coherent inspector lifecycle boundary. The pinned consumer enum and declarations are byte-identical in Home Debugger.zig and Bun src/jsc/Debugger.zig; the pinned Bun C++ bridge maps values 1–5 to JavaScriptCore inspector call types, returns immediately without an attached debugger agent, and forwards schedule/cancel/will-dispatch/did-dispatch to that agent.

Acceptance

  • Pin the exact Home/Bun declaration hashes and Bun BunDebugger.cpp forwarding semantics, including the enum(u8) values DOMTimer=1, EventListener=2, PostMessage=3, RequestAnimationFrame=4, and Microtask=5.
  • Implement Debugger__didScheduleAsyncCall, Debugger__didCancelAsyncCall, Debugger__willDispatchAsyncCall, and Debugger__didDispatchAsyncCall with the exact C ABI and same-VM global validation.
  • Preserve the no-agent fast no-op, including no dereference or enum assertion when no debugger is attached; with an agent, reject invalid call types as an ABI contract violation rather than silently aliasing them.
  • Track scheduled calls by (AsyncCallType, callbackId), preserve the scheduling stack and selected global, support repeated/non-single-shot dispatch, and retire single-shot and cancelled calls at the exact lifecycle boundary.
  • Make willDispatch install the matching async parent for debugger pauses/errors and make didDispatch restore nested/reentrant dispatch state without leaking one task's ancestry into another.
  • Define and test duplicate schedule, missing cancel/dispatch, sibling-realm, foreign-VM, detach/reattach, callback-id extremes, all five call types, and teardown behavior without retaining a dead realm or VM.
  • Extend the generated compile-link-runtime fixture through all four exports and add inspector protocol evidence that an async dispatch exposes the captured parent stack only while active.
  • Mark all four exports implemented in both inventories, preserve zero unclassified entries, and update the README/private ABI counts and evidence.

Design constraint

A symbol-only no-op is not completion. These hooks exist to preserve debugger async causality, but their bookkeeping must remain dormant when no inspector agent is attached and must never turn into a general runtime root.

Evidence

Completed on main in runtime commit ed109b6d with README/private-ABI evidence in 8ee1ef21.

  • docs/abi/debugger-async-call-contract.json pins both declaration files, Bun C++ forwarding, the five exact enum(u8) values, and the no-agent/invalid-agent boundaries.
  • Focused coverage exercises no-agent behavior, all call types, zero/max callback IDs, sibling and foreign VMs, duplicate/cancel/missing operations, nested LIFO dispatch, single-shot/repeated calls, protocol projection, disable, and teardown.
  • Full suite from the clean runtime commit: 989/989 passed, zero skipped, zero failed, zero leaked.
  • Generated Home consumer: 278/278 symbols linked; runtime matrix passed.
  • Home inventory: 276/431 implemented, 155 pending, zero unclassified.
  • Bun core inventory: 268/421 implemented, 153 pending, zero unclassified.
  • Public/private export audit: 117 public functions complete and 284 classified private profile exports.
  • The committed 1,540-sample benchmark matrix remains current because these dormant/inspector-only hooks are outside its workloads.
9 days ago

Implemented and documented on main in ed109b6d and 8ee1ef21. All acceptance gates are green: 989/989 full tests with zero leaks, 278/278 generated consumer symbols, exact pinned enum/forwarding contract, protocol async-stack evidence, and zero inventory drift or unclassified entries.

Sign in to comment on this issue.