Skip to content

batch: land #8825, #8826, #8830, #8832 - #8835

Merged
proggeramlug merged 19 commits into
mainfrom
merge/batch-8825-8832
Aug 25, 2026
Merged

batch: land #8825, #8826, #8830, #8832#8835
proggeramlug merged 19 commits into
mainfrom
merge/batch-8825-8832

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Batch landing of four reviewed PRs, validated once as a single merged tree.

PR
#8825 fix(async_hooks): land remaining lifecycle follow-ups
#8826 perf(codegen): specialize branded ReadonlySet.has
#8830 perf(map): specialize declared Map.get dispatch
#8832 runtime(gc): resolve x19 frame-base roots in the fast fp-chain walk

#8833 is deliberately excluded — see below.

Audit notes

#8832 is the GC-critical one. It accepts DWARF reg 19 as a frame base only when a per-frame x19_is_body_sp() re-decodes the prologue and confirms the exact mov x19, sp encoding (0x910003F3) after the frame pointer — the walker's anchor — is established. Every branch fails closed: return fp_set if the capture precedes the anchor, return false on any unexpected sp write (realigning and sp, dynamic sub sp, sp, xN), and a default false if the bounded 24-instruction window finds nothing. A frame that fails the check falls back to the platform unwinder, i.e. today's behaviour.

#8830 was gate-flagged by local_binding_type_audit for an unclassified local_type_hint read in is_declared_map_expr. Verified the whole chain before classifying it runtime-validated rather than waiving it: codegen uses the declared type only to select js_declared_map_get, which brand-checks the live receiver (try_read_gc_header + obj_type == GC_TYPE_MAP) before the fast path, and on a brand miss roots both operands in a handle scope and refreshes them before falling back to ordinary js_native_call_method dispatch. The declared type never becomes a layout proof in emitted code — the #7773 discipline holds.

Validation (merged tree)

  • all 30 lint-job gates pass
  • perry-runtime 2694, perry-codegen 1257, perry-stdlib 120, perry-hir 336 — all 0 failed
  • moving-GC arm (PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1), run specifically for runtime(gc): resolve x19 frame-base roots in the fast fp-chain walk (#8770) #8832: 2678 passed / 16 failed — identical failure count to clean main, so those 16 are pre-existing behaviour of those knobs and none is introduced here
  • df checked before and after; no result produced under ENOSPC

Why #8833 is not in this batch

perf: complete ECS benchmark specializations fails two perry-codegen tests. Attributed by A/B on clean trees from the same base:

clean main            1253 passed; 0 failed
main + #8833 alone    1257 passed; 2 failed
  • collectors::proven_args::tests::pshape_argument_symbol_reachability$pshape_args symbol fragments now appear in collectors/hir_facts.rs and collectors/ptr_shape_report.rs, outside the direct-call allowlist.
  • collectors::ptr_shape::opt_report_tests::escape_kinds_are_discriminated — the call-argument escape reason changed to "passed to a call that does not preserve whole-lifetime containment. A guarded $pshape_args route may still specialize field reads performed before the callee publishes the argument…", which no longer contains the substring the test asserts.

The second is not obviously a stale-test problem: the new wording makes a substantive claim about what a guarded route may still specialize after a call, and updating the assertion to match would rubber-stamp that claim. That is the author's call, not mine.

Summary by CodeRabbit

  • New Features

    • Improved Map.get, ReadonlyMap.get, and ReadonlySet.has handling, including custom objects, subclasses, and nullable values.
    • Enhanced async-resource and event-emitter lifecycle behavior, including safer context restoration and event-name handling.
    • Added more accurate socket write tracking, including queued bytes and completion errors.
  • Bug Fixes

    • Improved reliability for zlib, DNS, HTTP, WebCrypto, worker, and network operations during garbage collection or errors.
    • Corrected deferred cleanup and callback behavior across asynchronous operations.
  • Tests

    • Expanded coverage for collection dispatch, async hooks, socket writes, and resource lifecycle edge cases.

Ralph Küpper added 19 commits August 25, 2026 15:06
…8770)

LLVM takes x19 as a frame base pointer for a function with a dynamic stack
allocation (a VLA or a spread-argument area). Its GC roots are stack slots
addressed via x19, and x19 is established as `mov x19, sp` immediately after
the fixed prologue and before the dynamic `sub sp, sp, xN`, with no
realignment — so x19 holds exactly the body SP the fp chain already
reconstructs (`fp - fp_to_sp_offset`).

Before this, any x19 root flipped the whole-image `chain_walkable` flag false
(it required EVERY root to be fp/sp), which globally disabled the fast x29-chain
root walk and forced every GC onto the platform unwinder. In cli.js just 63 of
72,812 functions use an x19 base, yet they disabled the correct fast walker for
all of them; the unwinder then mis-resolved compiled-JS stack-slot roots and
live young objects were swept (0xff-poison-receiver SIGSEGV /
`(number).get is not a function`).

Make `chain_walkable` accept x19 and resolve an x19 root like an SP root, gated
per frame by `x19_is_body_sp` (confirms the `mov x19, sp` prologue shape); a
frame that does not match still fails closed to the unwinder. Any other base
register still disables the chain walk.

Claude-Session: https://claude.ai/code/session_01TwxRkALrR9HKSF1zKLSTAF
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f65af584-9e43-4bed-9627-9603ce9d9453

📥 Commits

Reviewing files that changed from the base of the PR and between 88583f0 and 7fa3a2f.

📒 Files selected for processing (46)
  • changelog.d/8825-async-hooks-lifecycle.md
  • changelog.d/8826-readonly-set-has.md
  • changelog.d/8830-declared-map-get-dispatch.md
  • crates/perry-codegen/src/expr/mod.rs
  • crates/perry-codegen/src/expr/readonly_collection_tests.rs
  • crates/perry-codegen/src/expr/this_super_call.rs
  • crates/perry-codegen/src/lower_call/builtin.rs
  • crates/perry-codegen/src/lower_call/property_get/map_set.rs
  • crates/perry-codegen/src/runtime_decls/strings.rs
  • crates/perry-codegen/src/type_analysis.rs
  • crates/perry-codegen/src/type_analysis/strings.rs
  • crates/perry-ext-events/src/emit_scope.rs
  • crates/perry-ext-events/src/lib.rs
  • crates/perry-ext-http/src/lib.rs
  • crates/perry-ext-http/src/server/handle_dispatch.rs
  • crates/perry-ext-net/src/adopt.rs
  • crates/perry-ext-net/src/ipc.rs
  • crates/perry-ext-net/src/lib.rs
  • crates/perry-ext-net/src/lifecycle.rs
  • crates/perry-ext-net/src/server_state.rs
  • crates/perry-ext-zlib/src/stream.rs
  • crates/perry-runtime/src/async_hooks.rs
  • crates/perry-runtime/src/async_hooks/provider_ffi.rs
  • crates/perry-runtime/src/async_hooks/scopes.rs
  • crates/perry-runtime/src/async_hooks/test_support.rs
  • crates/perry-runtime/src/gc/roots/stack_maps.rs
  • crates/perry-runtime/src/gc/roots/stack_maps_decode_tests.rs
  • crates/perry-runtime/src/gc/tests/runtime_roots/hook_dispatch_handles.rs
  • crates/perry-runtime/src/map.rs
  • crates/perry-runtime/src/set.rs
  • crates/perry-stdlib/src/webcrypto/digest.rs
  • crates/perry-stdlib/src/worker_threads/worker_pump.rs
  • crates/perry-stdlib/src/zlib.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry/src/commands/compile/run_pipeline.rs
  • crates/perry/tests/declared_map_branded_dispatch.rs
  • crates/perry/tests/readonly_set_branded_dispatch.rs
  • crates/perry/tests/source_graph_export_regressions.rs
  • scripts/gc_runtime_root_holders.json
  • scripts/local_binding_type_allowlist.json
  • scripts/raw_handle_debt_baseline.txt
  • scripts/raw_handle_debt_files.txt
  • scripts/thread_local_cold_allowlist.json
  • test-parity/node-suite/async_hooks/integrations/events-emitter.ts
  • test-parity/node-suite/async_hooks/providers/net-write-callbacks.ts
  • test-parity/node-suite/async_hooks/resource/shadowed-spread-parent.ts

📝 Walkthrough

Walkthrough

This change adds exception-safe async-resource lifecycle handling, GC-safe callback and argument rooting, branded Map and Set dispatch, AArch64 stack-root support, corrected socket byte accounting, and related compiler, runtime, integration, and parity tests.

Changes

Async lifecycle and GC safety

Layer / File(s) Summary
Exception-safe resource scopes
crates/perry-runtime/src/async_hooks/*, crates/perry-stdlib/src/worker_threads/worker_pump.rs
Async-resource entry, completion, exit, destruction, and error propagation now restore execution and async-local state when hooks or callbacks throw.
Async-resource rooting and dispatch
crates/perry-codegen/src/expr/this_super_call.rs, crates/perry-codegen/src/lower_call/builtin.rs, crates/perry-runtime/src/async_hooks.rs, crates/perry-runtime/src/gc/tests/runtime_roots/hook_dispatch_handles.rs
Async-resource receivers, callbacks, argument arrays, and allocation-sensitive values now use rooted handles and refreshed values.
Event and zlib lifecycle dispatch
crates/perry-ext-events/src/*, crates/perry-ext-http/src/*, crates/perry-ext-zlib/src/stream.rs, crates/perry-stdlib/src/zlib.rs, test-parity/node-suite/async_hooks/integrations/events-emitter.ts
Event and zlib callbacks now use rooted values, thunk-based provider execution, fallible scopes, and explicit deferred-destroy phases.
AArch64 stack-map root walking
crates/perry-runtime/src/gc/roots/stack_maps.rs, crates/perry-runtime/src/gc/roots/stack_maps_decode_tests.rs
Fast stack walking now supports validated x19 body-stack-pointer roots and rejects unsupported prologues.

Branded collection dispatch

Layer / File(s) Summary
Collection type analysis
crates/perry-codegen/src/type_analysis/*.rs
The compiler identifies declared Map, ReadonlyMap, and ReadonlySet expressions across nested type structures.
Collection codegen and runtime paths
crates/perry-codegen/src/lower_call/property_get/map_set.rs, crates/perry-codegen/src/runtime_decls/strings.rs, crates/perry-runtime/src/map.rs, crates/perry-runtime/src/set.rs
Native collection brands use direct helpers. Other receivers use rooted ordinary method dispatch.
Type-only metadata and dispatch validation
crates/perry/src/commands/compile/*, crates/perry-codegen/src/expr/readonly_collection_tests.rs, crates/perry/tests/*dispatch*.rs, crates/perry/tests/source_graph_export_regressions.rs
Type-only class imports retain compile-time metadata without runtime initialization. Tests cover native, structural, nullable, subclass, and cross-module cases.

Socket write accounting

Layer / File(s) Summary
Queued-byte state and write wiring
crates/perry-ext-net/src/{adopt.rs,ipc.rs,lib.rs,server_state.rs}
Socket states initialize bytes_queued, write commands use the lifecycle helper, and closure clears queued bytes.
Asynchronous socket lifecycle
crates/perry-ext-net/src/lifecycle.rs, test-parity/node-suite/async_hooks/providers/net-write-callbacks.ts
Queued and dispatched byte counts now update with successful enqueue and partial transport progress. Failed or closed writes complete callbacks with errors.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AsyncResource
  participant ResourceScopes
  participant LifecycleHooks
  participant Callback
  AsyncResource->>ResourceScopes: run_resource_scope_catching
  ResourceScopes->>LifecycleHooks: before
  ResourceScopes->>Callback: execute callback
  ResourceScopes->>LifecycleHooks: after
  ResourceScopes-->>AsyncResource: restore state and return result
Loading

Suggested reviewers: thehypnoo

✨ 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 merge/batch-8825-8832

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.

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