Skip to content

N-API scope-bound value lifetimes (Locals + generation-tagged ids) - #41

Merged
Arshia001 merged 3 commits into
cross-vm-resource-limitsfrom
napi-scope-bound-lifetimes
Aug 7, 2026
Merged

N-API scope-bound value lifetimes (Locals + generation-tagged ids)#41
Arshia001 merged 3 commits into
cross-vm-resource-limitsfrom
napi-scope-bound-lifetimes

Conversation

@Arshia001

Copy link
Copy Markdown
Member

Stack 2/4 (napi). Base: cross-vm-resource-limits. → next: napi-v8-imports-bridge-fixes.

Reworks N-API value lifetimes from persist-everything to scope-bound Locals:

  • generation-tagged value ids + env-pointer gating on live-env membership
  • flip value lifetimes to scope-bound Locals (handle-scope stack, implicit per-callback escapable frame, LIFO enforcement)
  • scope-lifetime conformance test (stale-id rejection, ref survival, LIFO mismatch, churn)
  • rustc 1.94 pin for standalone builds

⚠️ Stacked chain, merge bottom-up; not yet mergeable.

Arshia001 and others added 3 commits July 28, 2026 14:51
…ership

Replace the flat values map with a slot table whose u32 ids encode
((slot_index + 1) << 12) | generation. Stale or forged ids now fail the
generation check in LoadValue and surface as napi_invalid_arg instead of
resolving to an unrelated value. Still ref-backed: no lifetime change,
and all mint/resolve sites are untouched since the id stays a u32.

Also gate LookupEnvState on g_envs membership so a stale or forged
SnapiEnvState pointer from the guest is rejected before the first
dereference. Groundwork for the scope-bound value-lifetime rework.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Value slots now store raw napi_values owned by a per-env stack of scope
frames instead of pinning every value with a persistent reference:

- guest napi_open/close_handle_scope become real: they push/pop frames
  backed by heap napi handle scopes, with LIFO enforced as a safe
  napi_handle_scope_mismatch error. Closing a frame retires its ids
  (generation bump) before the backing Locals die with the napi scope.
- generic_wasm_callback pushes an implicit escapable frame per callback,
  so argument/intermediate/return ids die when the callback returns; the
  return value is escaped into the enclosing scope for V8's trampoline.
  WasmInterruptCallback gets the same treatment with a plain frame.
- escape_handle mints the escaped value into the parent frame of the
  escapable scope; get_reference_value keeps minting into the current
  frame. Cross-scope retention remains the napi_ref table.
- Teardown drains frames instead of deleting per-value references.
- The Rust layer prunes stale guest_data_ptrs mappings (amortized, only
  ids dead in every registered env) and gains a live-value-count debug
  getter for leak assertions.

The count-cap remains as a backstop bound on live slots.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Arshia001
Arshia001 force-pushed the napi-scope-bound-lifetimes branch from bac2e66 to ef4b722 Compare July 28, 2026 14:52
@Arshia001
Arshia001 merged commit 3745fe0 into main Aug 7, 2026
3 of 8 checks passed
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.

2 participants