Skip to content

fix(node:async_hooks): createHook/AsyncResource validation + ALS arg forwarding#3520

Merged
proggeramlug merged 1 commit into
mainfrom
fix-asynchooks-3089-3090-3091-3093
May 31, 2026
Merged

fix(node:async_hooks): createHook/AsyncResource validation + ALS arg forwarding#3520
proggeramlug merged 1 commit into
mainfrom
fix-asynchooks-3089-3090-3091-3093

Conversation

@proggeramlug
Copy link
Copy Markdown
Contributor

Closes #3089
Closes #3093

Summary

Brings node:async_hooks createHook validation and AsyncLocalStorage argument forwarding to Node parity.

  • node:async_hooks: validate createHook options and callbacks #3089 — createHook options/member validation. async_hooks.createHook(options) now rejects a nullish top-level value with Node's plain TypeError (no error code) — Cannot destructure property 'init' of 'undefined' as it is undefined. / ... of 'object null' as it is null. — before any callback is read. A present (non-undefined) init/before/after/destroy/promiseResolve member that is not callable now throws TypeError [ERR_ASYNC_CALLBACK] with hook.<name> must be a function. Missing/undefined members and non-nullish primitive top-level options (e.g. 0) stay allowed, matching Node's destructuring semantics.
  • node:async_hooks: forward AsyncLocalStorage run and exit arguments #3093 — ALS run/exit rest-argument forwarding. AsyncLocalStorage#run(store, callback, ...args) and #exit(callback, ...args) now forward the trailing rest arguments to the callback. The native-table rows gained a trailing NA_VARARGS slot (already-existing codegen lowering packs ...args into a JS array); the runtime helpers invoke the validated closure via js_closure_call_array with the forwarded args while preserving store push/pop (run) and store save/clear/restore (exit) semantics.

Implementation

  • crates/perry-runtime/src/async_hooks.rs: validate_create_hook_options (nullish guard) + validate_hook_member (present-non-callable guard), wired into js_async_hooks_create_hook / callbacks_from_options.
  • crates/perry-stdlib/src/async_local_storage.rs: js_async_local_storage_run/_exit take a trailing args_array: i64 and call the callback through call_with_forwarded_args (js_closure_call_array).
  • crates/perry-codegen/src/lower_call/native_table/async_decimal.rs: run&[NA_F64, NA_F64, NA_VARARGS], exit&[NA_F64, NA_VARARGS].

No new HIR variant; no new codegen-emitted #[no_mangle] (only signatures of existing emitted helpers changed). #3090/#3091 were already closed on main (AsyncResource constructor + callback validation present in async_hooks.rs); this PR's gap test re-covers them as a regression net but does not re-close them.

Validation

test-files/test_gap_asynchooks_3089_3090_3091_3093.ts (imports node:async_hooks, prints err.name/code/message for throws and the forwarded args + return values for ALS run/exit) is byte-identical to node --experimental-strip-types under the DEFAULT auto-optimize compile.

Guards: ./scripts/check_file_size.sh (exit 0), cargo fmt --all -- --check (clean), cargo test --release -p perry-hir (green), cargo test --release -p perry-runtime async_hooks (green single-threaded; the test_async_hooks_promise_alloc_remains_malloc_tracked failure under parallel --test-threads is pre-existing global-HOOKS_ACTIVE cross-test contamination, passes isolated and is unrelated to this change).

- createHook(options) now rejects nullish top-level options with Node's
  destructure TypeError (no code) and rejects present non-callable
  init/before/after/destroy/promiseResolve members with
  TypeError [ERR_ASYNC_CALLBACK] 'hook.<name> must be a function'.
  Missing/undefined members and non-nullish primitive options stay allowed.
- AsyncLocalStorage#run(store, cb, ...args) and #exit(cb, ...args) now
  forward the trailing rest arguments to the callback via a new NA_VARARGS
  native-table slot + js_closure_call_array, preserving store push/pop and
  exit restoration semantics.
- Add gap test test_gap_asynchooks_3089_3090_3091_3093.ts (byte-identical
  to node --experimental-strip-types).
@proggeramlug proggeramlug merged commit 348c27a into main May 31, 2026
11 checks passed
@proggeramlug proggeramlug deleted the fix-asynchooks-3089-3090-3091-3093 branch May 31, 2026 04:25
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.

node:async_hooks: forward AsyncLocalStorage run and exit arguments node:async_hooks: validate createHook options and callbacks

1 participant