Skip to content

fix(perry-ext-events): #1274 sync emit signature with perry-stdlib (variadic args) - #1289

Merged
proggeramlug merged 1 commit into
mainfrom
fix/1274-eventemitter-numeric-payload
May 21, 2026
Merged

fix(perry-ext-events): #1274 sync emit signature with perry-stdlib (variadic args)#1289
proggeramlug merged 1 commit into
mainfrom
fix/1274-eventemitter-numeric-payload

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

  • import 'events' / import 'node:events' routes to perry-ext-events via well_known_bindings.toml. Codegen's native_table lowers emit with NA_VARARGS + NR_F64 (post-PR Add granular node events parity suite #1186), but perry-ext-events still had the legacy (handle, name, arg: f64) -> bool ABI. The args-array pointer thus got reinterpreted as arg: f64; high heap-pointer bits satisfy the IEEE 754 NaN tag, so every listener observed NaN instead of the real payload.
  • Synced perry-ext-events' js_event_emitter_emit + _emit0 to perry-stdlib's signatures (args_ptr ArrayHeader; f64 NaN-boxed bool return); updated drain_pending_once_promises to pass the full args array so events.once resolves with the full tuple per Node.
  • Closes EventEmitter listeners: mutable captures don't receive numeric/object payload arguments (NaN/undefined) #1274 (test_express_mount + test_issue_850_eventemitter parity blockers from v0.5.1019).

Local validation (macOS arm64)

Min repro before fix:  inside listener, x: NaN
Min repro after fix:   inside listener, x: 7

5 v0.5.1019 parity tests:
  test_edge_buffer_from_encoding   PASS  (already closed by #982ed7a2)
  test_express_mount               PASS  ← was FAIL
  test_issue_850_eventemitter      PASS  ← was FAIL
  test_issue_584_text_encoder      PASS  (already closed by #982ed7a2)
  test_gap_console_methods         FAIL  (tracked under #1277 console.time)

Granular events parity suite (test-parity/node-suite/events/**):
  Before fix:  18 passed, 19 failed
  After fix:   26 passed, 11 failed   (-8 net failures; no new regressions)

The remaining 11 events failures are pre-existing perry-ext-events behavior gaps (setMaxListeners ignored, newListener/removeListener meta-events not emitted, error-without-listener doesn't throw) — unrelated to this signature fix.

Test plan

  • cargo fmt --all -- --check clean
  • cargo build --release -p perry-ext-events clean
  • test_express_mount and test_issue_850_eventemitter pass locally
  • Events parity suite: 26/37 pass (up from 18/37) — no regression
  • CI parity gate passes

…ariadic args)

`import 'events'` and `import 'node:events'` route to perry-ext-events
through `well_known_bindings.toml`, so listeners installed via the
`node:events` shim ended up dispatching against perry-ext-events'
`js_event_emitter_emit` — not perry-stdlib's. PR #1186 changed the
perry-stdlib signature from

    fn js_event_emitter_emit(handle, name, arg: f64) -> bool

to

    fn js_event_emitter_emit(handle, name, args_ptr: *mut ArrayHeader) -> f64

so the codegen native_table now lowers `emit` calls with `NA_VARARGS`
(single `*mut ArrayHeader` slot) and `NR_F64` return. perry-ext-events
was never updated to match — when the linker resolves to its old
single-`arg: f64` symbol, the args-array POINTER value gets reinterpreted
as `arg: f64`. High-bit heap pointers happen to satisfy the IEEE 754
NaN tag pattern, so every listener observes `NaN` instead of the real
payload — `test_express_mount` saw `hitCount: NaN` / `parentName:
undefined`, `test_issue_850_eventemitter` saw `once values: [ NaN ]` /
`addListener/removeListener: [ NaN ]`.

Mirrors perry-stdlib::events exactly:

- `js_event_emitter_emit`: takes `args_ptr: *mut ArrayHeader`, returns
  `f64` (NaN-boxed `TAG_TRUE` / `TAG_FALSE`). Extracts `args_ptr[0]`
  via `js_array_get` for the closure-call-1 dispatch.
- `js_event_emitter_emit0`: same return-type swap (bool → f64) so both
  emit variants round-trip through codegen's f64 ABI.
- `drain_pending_once_promises`: takes the full args array instead of
  synthesizing a 1-element `[arg]` wrap — matches Node's `events.once`
  semantics where the resolution value is the full args tuple, and
  matches perry-stdlib's behaviour.

Local repro on macOS arm64 after rebuild:

  Before fix: `inside listener, x: NaN`
  After fix:  `inside listener, x: 7`

Events parity suite (`test-parity/node-suite/events/**`):

  Before fix: 18 passed, 19 failed
  After fix:  26 passed, 11 failed  (-8 regressions cleared)

The remaining 11 failures are pre-existing perry-ext-events behavior
gaps (setMaxListeners ignored, newListener/removeListener meta-events
not emitted, `error`-without-listener doesn't throw, etc.) — unrelated
to this signature fix.

Closes #1274.
@proggeramlug
proggeramlug merged commit b8977a1 into main May 21, 2026
9 checks passed
@proggeramlug
proggeramlug deleted the fix/1274-eventemitter-numeric-payload branch May 21, 2026 19:22
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.

EventEmitter listeners: mutable captures don't receive numeric/object payload arguments (NaN/undefined)

1 participant