ReviewOS

zig-utils/zig-js

Private ABI: URLSearchParams create/fromJS/toString boundary

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

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

Symbols

SymbolHome (packages/runtime/src/jsc/URLSearchParams.zig @ 7ed99c02)Bun C++ (upstream/src/jsc/bindings/URLSearchParams.cpp)
URLSearchParams__create:20 — (globalObject: *JSGlobalObject, *const ZigString) JSValue:34 — URLSearchParams::create(str, nullptr) wrapped via toJSNewlyCreated
URLSearchParams__fromJS:26 — (JSValue) ?*URLSearchParams:41 — WebCoreCast
URLSearchParams__toString:32 — (self, ctx, callback) void:49 — serialize, hand out as ZigString via callback

The zig-js engine already ships the URLSearchParams JS builtin (form-urlencoded parse with one leading ? stripped, +/percent codec, native serializer, internal \x00usp pairs slot), so this slice is thin ABI plumbing over existing engine semantics — not a new URL implementation.

Acceptance criteria

  • URLSearchParams__create decodes the input ZigString and constructs a realm-prototype URLSearchParams instance from the query string (same semantics as new URLSearchParams(str)), returning the encoded object; engine-visible as instanceof URLSearchParams with working .get/.toString.
  • URLSearchParams__fromJS returns the stable canonical cell handle for values carrying the engine's URLSearchParams internal slot (native-created or JS-constructed, either realm of the group), null for anything else (plain objects, strings, numbers, empty/undefined).
  • URLSearchParams__toString serializes exactly like the JS toString method and invokes the callback once with a stable borrowed ZigString view (latin-1 when the serialization is 8-bit); null params/callback tolerated without invocation.
  • Engine helpers added as narrow pub wrappers next to the existing USP implementation (constructor setup shared, not duplicated).
  • Compiled runtime fixture covers: create→fromJS→toString roundtrip with exact +/percent re-encoding, JS-constructed instance acceptance, negative fromJS matrix, empty-input empty serialization, null tolerance, instanceof/.get engine integration. Fixture symbol count 291 → 294.
  • Home inventory 291/431, Bun inventory 284/421; bun-private-abi-audit + c-api-audit green; full zig build test zero new failures (worktree baseline-vs-candidate against the same pinned base).

Design constraints

  • The URLSearchParams* native pointer of Bun maps to the canonical cell handle, same convention as JSC__JSObject__create (#306): zig-js has no separate native USP object — the JS object with its internal slot is the instance.
  • No WHATWG URL parser work is pulled in: URL.zig/DOMURL.zig stay deferred; this slice is strictly the form-urlencoded pairs boundary.
9 days ago

Evidence — closed as completed

Commits (on main):

  • 2e0322e1 feat(abi): expose the URLSearchParams pairs boundary (#307)
  • 437e6467 docs(abi): publish URLSearchParams boundary evidence (#307)

Implementation

  • src/interpreter.zig: extracted uspAllocate (realm prototype + \x00usp pairs slot setup) so the JS constructor and the c_api shim share instance setup; added narrow pub wrappers urlSearchParamsCreateFromString (allocate + uspParseString, one leading ? stripped per WebKit semantics) and urlSearchParamsSerialize (uspToString path).
  • src/c_api.zig: URLSearchParams__create (decode ZigString → realm-prototype instance, instanceof URLSearchParams with working methods), URLSearchParams__fromJS (object-with-\x00usp-slot discrimination → canonical cell handle, null otherwise), URLSearchParams__toString (owner realm recovered from the handle, serialize like the JS method, exactly one callback with a stable borrowed ZigString view). Null VM/handle/callback tolerated.

Validation

  • zig build test-home-private-abi → Home private value shims: 294/294 symbols linked; runtime matrix passed
  • Fixture coverage: create→fromJS→toString roundtrip with exact re-encode (?a=1&b=two+words&b=3&c=%41%2C → a=1&b=two+words&b=3&c=A%2C), .get/.getAll/instanceof engine integration, JS-constructed instance acceptance, negative fromJS matrix (plain object, string, int32, undefined, empty), empty-input empty serialization, foreign-realm create+serialize via owner-realm recovery, null tolerance.
  • bun-private-abi-audit → implemented-private=284, pending-private=137; Home audit implemented-private=291, pending-private=140; c-api audit 117/117 + 300 private exports; test-home-public-abi 50/50; zig build test -Dtest-filter=c_api 148/148.
  • Full zig build test worktree A/B vs pinned base e60432ab: base 1142 passed / 3 failed, candidate 1142 passed / 3 failed — identical failing set (the two known pre-existing gc/wasm.decode failures plus one pre-existing wasm.validate failure), zero new failures.

Sign in to comment on this issue.