Skip to content

Propose wasm-bindgen ownership boundary for #1678 - #4

Draft
jlucaso1 wants to merge 1 commit into
review-base/issue-1678-eca0cab8afrom
fix/issue-1678-finalization-registry
Draft

jlucaso1 wants to merge 1 commit into
review-base/issue-1678-eca0cab8afrom
fix/issue-1678-finalization-registry

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

wasm-bindgen initializes finalization registries before its WASM instance. Replacing the current refusal with an empty registry would hide lost Rust and closure cleanup, and running callbacks from native weak-disposal hooks would enter user code during teardown.

This draft records a focused ownership proposal for vercel-labs#1678 and adds three regression checks preserving the TypeScript diagnostic and JavaScript runtime fence. It does not implement FinalizationRegistry or close the issue. Internal review only; no upstream PR is authorized.

The proposal keeps the generated module, WASM instance, targets, held values, unregister tokens, and closures inside a Node island, with a copied JSON adapter for narrow operations. It distinguishes initialization from finalization, documents strong and weak edges, token semantics, collector-safe cleanup jobs, ProgramLifetime implications, and acceptance work. It builds on the existing libnode PRs instead of duplicating them. vercel-labs#1686 was merged into the loader branch, not main; the required loader, packs, and hosted-loop work remain outstanding.

Validation on upstream main eca0cab, in an isolated worktree:

  • Repository-pinned Node 24.22.0: the original reproduction prints ok; the current compiler still reports SC2020 for the type, constructor, and register call.
  • Reduced Node-only lifecycle experiment: minimal WASM initialization, shared-token unregister, unknown/repeated unregister, invalid callbacks/targets/tokens, target/held-value identity, and explicit binding disposal pass. No forced GC or finalizer deadline is asserted.
  • Separate Node 24.22.0 --expose-gc observation: one collectible target-as-token callback ran after the GC call stack returned; no callback ran for the retained target, the held-value-to-target cycle, or an unregistered target. Callback count/deadline was not a pass condition. This is a Node observation, not native collector or Rust-destructor evidence.
  • Actual published @oxidezap/whatsapp-rust-bridge@0.26.0 imports and computes SHA256 of abc under Node 24.22.0. This does not demonstrate native execution or full client compatibility.
  • Focused refusal and compiler ownership-policy tests: 4/4 pass in both plain and SCRIPTC_SAN=1 configurations on Node 24.22.0. These frontend-only tests do not exercise ASan or a native collector.
  • pnpm -r build, pnpm lint, pnpm format:check, pnpm test:ts7 --baselines-only, and git diff --check pass.

The issue remains unresolved. No failing-base/passing-native-patch result exists, because this draft intentionally leaves native behavior unchanged. Full native/sanitizer suites and zero-cost validation belong to an eventual implementation; they are not claimed by this proposal. Downloaded consumer artifacts and experimental scripts are excluded.

Review base: review-base/issue-1678-eca0cab8a points to the verified upstream main commit. Fork main is older; using a dedicated base keeps this internal PR limited to the two issue-specific files without changing fork main.

Compiled follow-up experiment, outside this PR

A separate worktree at existing upstream hosted-loop commit eaefbdf44 compiled a native ELF caller of @scriptc/node.callSync with AddressSanitizer and the runtime reference-count audit. It uses the branch-pinned, checksum-verified prebuilt libnode 24.22.1 and its bridge. No Node source build or FinalizationRegistry implementation was added.

The native executable loaded the exact published consumer inside Node, returned SHA256 of abc, ran the reduced registry-before-WASM and explicit-disposal experiment, and copied an invalid-byte RangeError back to native code. Its stdout matched the Node 24.22.0 oracle. The hosted pack is 24.22.1, as pinned by the experimental branch; this is a narrow smoke comparison, not a same-version full parity claim:

ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
{"wasm":true,"first":true,"second":false,"invalid":true,"allocations":0}
true

The direct host run exited 0. ASan emitted its existing makecontext/swapcontext support warning; it was not suppressed. The sandboxed run could not complete LeakSanitizer's ptrace-based inspection, so the same binary was rerun on the authorized host without changing security settings or disabling leak checks. This proves a narrow compiled caller plus in-process Node island route. It does not prove native lowering of the package/WASM/registry, Rust client lifecycle correctness, Baileyrs compatibility, or a passing full native/sanitizer suite.

The concrete native-language blockers are (1) ProgramLifetime instances have no collector header and empty retain/release helpers, so last-release observer hooks cannot discover their unreachable lifetime; (2) cycle free functions run while scr_collecting is true and dead members have been marked DOOMED, so arbitrary cleanup cannot run inline; (3) generated Rust closures use internal _wbg_cb_unref plus their own registry, not the public .free() method on class wrappers. A .free()-only convention does not establish complete ownership for this package.

The published consumer reports getWasmAllocationSnapshot().enabled === false; its zero allocation counters are not usable as evidence that Rust resources were reclaimed. A memory-profiled consumer build or another destructor-level observation is required for a stateful ownership claim.

The next shippable step is a stacked adapter/integration change on the existing Node-island stack, with an explicit JSON boundary and all resources remaining in Node. Shipping native FinalizationRegistry instead requires a separate language/collector decision, including how to handle ProgramLifetime and queued cleanup roots. This main-based draft intentionally does not import an API absent from main or duplicate the pending island infrastructure. Its scope remains partial and the issue remains open.

Tested adapter stack prepared for this PR

The working adapter and executable ownership tests are now published separately for focused review while the current head's CI finishes: 13-file comparison, head 0931187d2. This is not yet this PR's head, and no second PR was opened.

The new stack passed 12/12 focused tests in both plain and sanitizer configurations, covering release/dev, the exact consumer's public ESM export, counted-WASM class/closure ownership, explicit disposal/unregister after errors, interleaved async/sync calls, and callback/handle boundary refusals. The CLI example also runs with --libnode=beside. It remains a partial Node-owned compatibility path, not native FinalizationRegistry or complete client support.

After the existing run reaches a terminal state, this PR's review base/head will move to that focused stack. No run is being cancelled to make that update.

The original CI run is now terminal with failure: 50 jobs succeeded, while plain shard 8/20 ended in failure after its test step was marked cancelled, and the aggregate gate failed. The failed job log was unavailable from GitHub, so its cancellation cause is unverified. This front sent no cancellation. Upstream vercel-labs#1696 has since advanced to 049b34c1; the adapter stack is being revalidated against that existing fix before this PR's head/base update. The current head is still the original proposal; the focused stack comparison remains available above.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jlucaso1

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T00:14:48.951386Z 7c218d1 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 7c218d1f63

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant