Skip to content

fix(async_hooks): expose constructor prototypes - #7093

Merged
proggeramlug merged 4 commits into
mainfrom
fix/6764-async-hooks-prototype-metadata
Jul 31, 2026
Merged

fix(async_hooks): expose constructor prototypes#7093
proggeramlug merged 4 commits into
mainfrom
fix/6764-async-hooks-prototype-metadata

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • expose ordinary AsyncLocalStorage.prototype and AsyncResource.prototype objects with Node-compatible method names and arities
  • forward reflective prototype calls through the existing native async-hooks dispatcher while preserving variadic arguments
  • correct AsyncResource.length from 2 to 1 and cover constructor/prototype descriptors and real calls

This is a focused parity increment for the larger async_hooks umbrella; the remaining lifecycle/provider gaps stay tracked in #6764.

Refs #6764

Validation

  • cargo check -p perry-runtime
  • cargo test -p perry-runtime arity_table_matches_reference_exhaustively -- --test-threads=1
  • cargo test -p perry --test issue_6764_async_hooks_prototype_metadata -- --nocapture
  • ./run_parity_tests.sh --suite node-suite --module async_hooks --filter api-function-metadata (100%)
  • ./run_parity_tests.sh --suite node-suite --module async_hooks --filter run-nesting (100%)

Summary by CodeRabbit

  • Bug Fixes
    • Fixed node:async_hooks constructor and prototype metadata for AsyncLocalStorage and AsyncResource.
    • Corrected reflective prototype method behavior for run, runInAsyncScope, enterWith, and disable.
    • Updated the async_hooks callable arity for AsyncResource to match expected behavior.
    • Ensured enterWith/disable on unsupported (“foreign”) receivers consistently returns undefined.
  • Tests
    • Added a regression test covering async hooks prototype metadata and reflective method results.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds reflective prototype metadata and method dispatch for AsyncLocalStorage and AsyncResource, roots async-hooks attachment values, corrects AsyncResource arity metadata, and adds regression coverage.

Changes

Async hooks prototype support

Layer / File(s) Summary
Prototype construction and dispatch
crates/perry-runtime/src/object/native_module.rs, crates/perry-runtime/src/object/native_module/async_hooks_exports.rs
Defines prototype method metadata, constructs rooted native closures, forwards reflective calls, and handles enterWith/disable foreign-object behavior.
Constructor attachment and arity wiring
crates/perry-runtime/src/object/native_module/callable_exports.rs
Roots constructor and closure values during attachment, installs async-hooks static methods and names, and changes AsyncResource arity from 2 to 1.
Reflective metadata regression coverage
crates/perry/tests/issue_6764_async_hooks_prototype_metadata.rs, changelog.d/7093-async-hooks-prototypes.md
Tests constructor metadata, prototype method calls, and foreign-object results, and records the fix in the changelog.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AsyncHooksConstructor
  participant AsyncHooksPrototype
  participant PrototypeMethodThunk
  participant DynamicMethodDispatcher
  AsyncHooksConstructor->>AsyncHooksPrototype: attach rooted prototype and method closures
  AsyncHooksPrototype->>PrototypeMethodThunk: invoke reflective method with receiver and arguments
  PrototypeMethodThunk->>DynamicMethodDispatcher: forward method name and arguments
  DynamicMethodDispatcher-->>PrototypeMethodThunk: return method result
Loading

Possibly related PRs

Suggested labels: bug

Suggested reviewers: thehypnoo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: exposing async_hooks constructor prototypes.
Description check ✅ Passed The description covers summary, related issue, and validation; only some template sections are not fully filled out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6764-async-hooks-prototype-metadata

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/perry-runtime/src/object/native_module/async_hooks_exports.rs`:
- Around line 59-122: The allocating operations in attach_prototype can move the
constructor and prototype, leaving raw addresses stale. Root both values with
crate::gc::RuntimeHandleScope and reload their current addresses before each
reuse after js_object_alloc, js_string_from_bytes, and js_closure_alloc; in
crates/perry-runtime/src/object/native_module/callable_exports.rs lines
1683-1726, closure_addr requires no independent change because the upstream
rooting fix makes its reuse safe.
- Around line 43-52: Update the receiver guard for the enterWith/disable no-op
path to use the canonical is_plausible_heap_addr predicate instead of
!is_handle_band. Preserve the pointer check, ensuring only plausible generic
heap objects take the no-op return while handle-band values and addresses below
the heap floor fall through.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 202fbf56-7789-45cc-80a0-8e7ef1e73887

📥 Commits

Reviewing files that changed from the base of the PR and between c86835f and b76e286.

📒 Files selected for processing (5)
  • changelog.d/7093-async-hooks-prototypes.md
  • crates/perry-runtime/src/object/native_module.rs
  • crates/perry-runtime/src/object/native_module/async_hooks_exports.rs
  • crates/perry-runtime/src/object/native_module/callable_exports.rs
  • crates/perry/tests/issue_6764_async_hooks_prototype_metadata.rs

Comment thread crates/perry-runtime/src/object/native_module/async_hooks_exports.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/perry-runtime/src/object/native_module/callable_exports.rs (1)

125-134: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse set_bound_native_closure_name instead of duplicating it. Lines 125-134 reproduce the body of set_bound_native_closure_name (Lines 1464-1497), including the identical PropertyAttrs::new(false, false, true) descriptor, but drop the documented #6809 rationale. Passing the reloaded pointer to the existing helper keeps that invariant in one place.

♻️ Proposed refactor
-    crate::closure::closure_set_dynamic_prop(
-        closure_handle.get_raw_mut_ptr::<crate::closure::ClosureHeader>() as usize,
-        "name",
-        f64::from_bits(JSValue::string_ptr(name_handle.get_raw_mut_ptr()).bits()),
-    );
-    super::super::set_builtin_property_attrs(
-        closure_handle.get_raw_mut_ptr::<crate::closure::ClosureHeader>() as usize,
-        "name".to_string(),
-        super::super::PropertyAttrs::new(false, false, true),
-    );
+    set_bound_native_closure_name(
+        closure_handle.get_raw_mut_ptr::<crate::closure::ClosureHeader>(),
+        name,
+    );

Note: the helper allocates its own name string, so name_string/name_handle become unnecessary here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/object/native_module/callable_exports.rs` around
lines 125 - 134, Replace the duplicated name-property setup in the current
callable export path with the existing set_bound_native_closure_name helper,
passing the reloaded closure pointer as required. Remove the now-unnecessary
name_string/name_handle allocation and preserve the helper’s documented property
attributes and naming behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/perry-runtime/src/object/native_module/callable_exports.rs`:
- Around line 125-134: Replace the duplicated name-property setup in the current
callable export path with the existing set_bound_native_closure_name helper,
passing the reloaded closure pointer as required. Remove the now-unnecessary
name_string/name_handle allocation and preserve the helper’s documented property
attributes and naming behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aacb02c3-bffb-4e29-a456-787edd4a383a

📥 Commits

Reviewing files that changed from the base of the PR and between b76e286 and 12e9180.

📒 Files selected for processing (2)
  • crates/perry-runtime/src/object/native_module/async_hooks_exports.rs
  • crates/perry-runtime/src/object/native_module/callable_exports.rs

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Addressed the late CodeRabbit maintainability note in c3bb2c4: async_hooks static methods now reuse set_bound_native_closure_name. I also made the shared helper root/reload both the closure and allocated name so the refactor preserves relocation safety across its allocations. Validation: cargo fmt/check and the full issue_6764_async_hooks_prototype_metadata integration test (1 passed, 156.08s).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/perry-runtime/src/object/native_module/callable_exports.rs (1)

1460-1495: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Return or preserve the relocated closure handle.

This scope protects closure during allocation, but the function returns no updated pointer. Any caller that reuses its original raw pointer after this call may access stale memory if GC relocates the closure. Pass an outer RuntimeHandleScope and return the reloaded pointer, then update callers to use it.

Based on learnings, Rust stack locals are not GC roots; relocated values must be reloaded from their handles before reuse.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/object/native_module/callable_exports.rs` around
lines 1460 - 1495, Update the closure-name installation function around
RuntimeHandleScope and closure_handle so it accepts an outer RuntimeHandleScope
and returns the closure’s reloaded pointer after all allocations and property
updates. Ensure callers use the returned handle-derived pointer rather than
reusing their original raw closure pointer, preserving GC safety when the
closure relocates.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/perry-runtime/src/object/native_module/callable_exports.rs`:
- Around line 1460-1495: Update the closure-name installation function around
RuntimeHandleScope and closure_handle so it accepts an outer RuntimeHandleScope
and returns the closure’s reloaded pointer after all allocations and property
updates. Ensure callers use the returned handle-derived pointer rather than
reusing their original raw closure pointer, preserving GC safety when the
closure relocates.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ec5e6011-5bbf-47c9-a575-b76226155584

📥 Commits

Reviewing files that changed from the base of the PR and between 12e9180 and c3bb2c4.

📒 Files selected for processing (1)
  • crates/perry-runtime/src/object/native_module/callable_exports.rs

@proggeramlug
proggeramlug merged commit 78af38c into main Jul 31, 2026
7 checks passed
@proggeramlug
proggeramlug deleted the fix/6764-async-hooks-prototype-metadata branch July 31, 2026 01:10
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